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;
|
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 Internal
|
||||||
} // namespace Autotest
|
} // namespace Autotest
|
||||||
|
@@ -37,8 +37,10 @@ public:
|
|||||||
const QString outputString(bool selected) const override;
|
const QString outputString(bool selected) const override;
|
||||||
|
|
||||||
void setTestSetName(const QString &testSetName) { m_testSetName = testSetName; }
|
void setTestSetName(const QString &testSetName) { m_testSetName = testSetName; }
|
||||||
|
bool isDirectParentOf(const TestResult *other, bool *needsIntermediate) const override;
|
||||||
private:
|
private:
|
||||||
|
bool isTest() const { return m_testSetName.isEmpty(); }
|
||||||
|
bool isTestSet() const { return !m_testSetName.isEmpty(); }
|
||||||
QString m_testSetName;
|
QString m_testSetName;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -162,14 +162,18 @@ void QtTestOutputReader::processOutput(const QByteArray &outputLine)
|
|||||||
if (currentTag == QStringLiteral("TestCase")) {
|
if (currentTag == QStringLiteral("TestCase")) {
|
||||||
m_className = m_xmlReader.attributes().value(QStringLiteral("name")).toString();
|
m_className = m_xmlReader.attributes().value(QStringLiteral("name")).toString();
|
||||||
QTC_ASSERT(!m_className.isEmpty(), continue);
|
QTC_ASSERT(!m_className.isEmpty(), continue);
|
||||||
TestResultPtr testResult = TestResultPtr(new QtTestResult(m_className));
|
TestResultPtr testResult = TestResultPtr(createDefaultResult());
|
||||||
testResult->setResult(Result::MessageTestCaseStart);
|
testResult->setResult(Result::MessageTestCaseStart);
|
||||||
testResult->setDescription(tr("Executing test case %1").arg(m_className));
|
testResult->setDescription(tr("Executing test case %1").arg(m_className));
|
||||||
m_futureInterface.reportResult(testResult);
|
m_futureInterface.reportResult(testResult);
|
||||||
} else if (currentTag == QStringLiteral("TestFunction")) {
|
} else if (currentTag == QStringLiteral("TestFunction")) {
|
||||||
m_testCase = m_xmlReader.attributes().value(QStringLiteral("name")).toString();
|
m_testCase = m_xmlReader.attributes().value(QStringLiteral("name")).toString();
|
||||||
QTC_ASSERT(!m_testCase.isEmpty(), continue);
|
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->setResult(Result::MessageCurrentTest);
|
||||||
testResult->setDescription(tr("Entering test function %1::%2").arg(m_className,
|
testResult->setDescription(tr("Entering test function %1::%2").arg(m_className,
|
||||||
m_testCase));
|
m_testCase));
|
||||||
@@ -179,7 +183,6 @@ void QtTestOutputReader::processOutput(const QByteArray &outputLine)
|
|||||||
QTC_ASSERT(!m_duration.isEmpty(), continue);
|
QTC_ASSERT(!m_duration.isEmpty(), continue);
|
||||||
} else if (currentTag == QStringLiteral("Message")
|
} else if (currentTag == QStringLiteral("Message")
|
||||||
|| currentTag == QStringLiteral("Incident")) {
|
|| currentTag == QStringLiteral("Incident")) {
|
||||||
m_dataTag.clear();
|
|
||||||
m_description.clear();
|
m_description.clear();
|
||||||
m_duration.clear();
|
m_duration.clear();
|
||||||
m_file.clear();
|
m_file.clear();
|
||||||
@@ -189,15 +192,15 @@ void QtTestOutputReader::processOutput(const QByteArray &outputLine)
|
|||||||
m_result = TestResult::resultFromString(
|
m_result = TestResult::resultFromString(
|
||||||
attributes.value(QStringLiteral("type")).toString());
|
attributes.value(QStringLiteral("type")).toString());
|
||||||
m_file = decode(attributes.value(QStringLiteral("file")).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_file = constructSourceFilePath(m_buildDir, m_file);
|
||||||
}
|
|
||||||
m_lineNumber = attributes.value(QStringLiteral("line")).toInt();
|
m_lineNumber = attributes.value(QStringLiteral("line")).toInt();
|
||||||
} else if (currentTag == QStringLiteral("BenchmarkResult")) {
|
} else if (currentTag == QStringLiteral("BenchmarkResult")) {
|
||||||
const QXmlStreamAttributes &attributes = m_xmlReader.attributes();
|
const QXmlStreamAttributes &attributes = m_xmlReader.attributes();
|
||||||
const QString metric = attributes.value(QStringLiteral("metric")).toString();
|
const QString metric = attributes.value(QStringLiteral("metric")).toString();
|
||||||
const double value = attributes.value(QStringLiteral("value")).toDouble();
|
const double value = attributes.value(QStringLiteral("value")).toDouble();
|
||||||
const int iterations = attributes.value(QStringLiteral("iterations")).toInt();
|
const int iterations = attributes.value(QStringLiteral("iterations")).toInt();
|
||||||
|
m_dataTag = attributes.value(QStringLiteral("tag")).toString();
|
||||||
m_description = constructBenchmarkInformation(metric, value, iterations);
|
m_description = constructBenchmarkInformation(metric, value, iterations);
|
||||||
m_result = Result::Benchmark;
|
m_result = Result::Benchmark;
|
||||||
} else if (currentTag == QStringLiteral("DataTag")) {
|
} else if (currentTag == QStringLiteral("DataTag")) {
|
||||||
@@ -250,30 +253,31 @@ void QtTestOutputReader::processOutput(const QByteArray &outputLine)
|
|||||||
m_cdataMode = None;
|
m_cdataMode = None;
|
||||||
const QStringRef currentTag = m_xmlReader.name();
|
const QStringRef currentTag = m_xmlReader.name();
|
||||||
if (currentTag == QStringLiteral("TestFunction")) {
|
if (currentTag == QStringLiteral("TestFunction")) {
|
||||||
if (!m_duration.isEmpty()) {
|
QtTestResult *testResult = createDefaultResult();
|
||||||
QtTestResult *testResult = new QtTestResult(m_className);
|
testResult->setResult(Result::MessageTestCaseEnd);
|
||||||
testResult->setFunctionName(m_testCase);
|
testResult->setDescription(
|
||||||
testResult->setResult(Result::MessageInternal);
|
m_duration.isEmpty() ? tr("Test function finished.")
|
||||||
testResult->setDescription(tr("Execution took %1 ms.").arg(m_duration));
|
: tr("Execution took %1 ms.").arg(m_duration));
|
||||||
m_futureInterface.reportResult(TestResultPtr(testResult));
|
m_futureInterface.reportResult(TestResultPtr(testResult));
|
||||||
}
|
|
||||||
m_futureInterface.setProgressValue(m_futureInterface.progressValue() + 1);
|
m_futureInterface.setProgressValue(m_futureInterface.progressValue() + 1);
|
||||||
|
m_dataTag.clear();
|
||||||
|
m_testCase.clear();
|
||||||
} else if (currentTag == QStringLiteral("TestCase")) {
|
} else if (currentTag == QStringLiteral("TestCase")) {
|
||||||
QtTestResult *testResult = new QtTestResult(m_className);
|
QtTestResult *testResult = createDefaultResult();
|
||||||
testResult->setResult(Result::MessageTestCaseEnd);
|
testResult->setResult(Result::MessageTestCaseEnd);
|
||||||
testResult->setDescription(
|
testResult->setDescription(
|
||||||
m_duration.isEmpty() ? tr("Test finished.")
|
m_duration.isEmpty() ? tr("Test finished.")
|
||||||
: tr("Test execution took %1 ms.").arg(m_duration));
|
: tr("Test execution took %1 ms.").arg(m_duration));
|
||||||
m_futureInterface.reportResult(TestResultPtr(testResult));
|
m_futureInterface.reportResult(TestResultPtr(testResult));
|
||||||
} else if (validEndTags.contains(currentTag.toString())) {
|
} else if (validEndTags.contains(currentTag.toString())) {
|
||||||
QtTestResult *testResult = new QtTestResult(m_className);
|
QtTestResult *testResult = createDefaultResult();
|
||||||
testResult->setFunctionName(m_testCase);
|
|
||||||
testResult->setDataTag(m_dataTag);
|
|
||||||
testResult->setResult(m_result);
|
testResult->setResult(m_result);
|
||||||
testResult->setFileName(m_file);
|
testResult->setFileName(m_file);
|
||||||
testResult->setLine(m_lineNumber);
|
testResult->setLine(m_lineNumber);
|
||||||
testResult->setDescription(m_description);
|
testResult->setDescription(m_description);
|
||||||
m_futureInterface.reportResult(TestResultPtr(testResult));
|
m_futureInterface.reportResult(TestResultPtr(testResult));
|
||||||
|
if (currentTag == QStringLiteral("Incident"))
|
||||||
|
m_dataTag.clear();
|
||||||
}
|
}
|
||||||
break;
|
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 Internal
|
||||||
} // namespace Autotest
|
} // namespace Autotest
|
||||||
|
@@ -33,6 +33,8 @@
|
|||||||
namespace Autotest {
|
namespace Autotest {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
|
class QtTestResult;
|
||||||
|
|
||||||
class QtTestOutputReader : public TestOutputReader
|
class QtTestOutputReader : public TestOutputReader
|
||||||
{
|
{
|
||||||
Q_DECLARE_TR_FUNCTIONS(Autotest::Internal::QtTestOutputReader)
|
Q_DECLARE_TR_FUNCTIONS(Autotest::Internal::QtTestOutputReader)
|
||||||
@@ -45,6 +47,8 @@ protected:
|
|||||||
void processOutput(const QByteArray &outputLine) override;
|
void processOutput(const QByteArray &outputLine) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QtTestResult *createDefaultResult() const;
|
||||||
|
|
||||||
enum CDATAMode
|
enum CDATAMode
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
|
@@ -25,6 +25,8 @@
|
|||||||
|
|
||||||
#include "qttestresult.h"
|
#include "qttestresult.h"
|
||||||
|
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
namespace Autotest {
|
namespace Autotest {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
@@ -71,5 +73,47 @@ const QString QtTestResult::outputString(bool selected) const
|
|||||||
return output;
|
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 Internal
|
||||||
} // namespace Autotest
|
} // namespace Autotest
|
||||||
|
@@ -39,7 +39,14 @@ public:
|
|||||||
void setFunctionName(const QString &functionName) { m_function = functionName; }
|
void setFunctionName(const QString &functionName) { m_function = functionName; }
|
||||||
void setDataTag(const QString &dataTag) { m_dataTag = dataTag; }
|
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:
|
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_function;
|
||||||
QString m_dataTag;
|
QString m_dataTag;
|
||||||
};
|
};
|
||||||
|
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include "testresult.h"
|
#include "testresult.h"
|
||||||
|
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/theme/theme.h>
|
#include <utils/theme/theme.h>
|
||||||
|
|
||||||
namespace Autotest {
|
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 Internal
|
||||||
} // namespace Autotest
|
} // namespace Autotest
|
||||||
|
@@ -59,6 +59,7 @@ enum Type {
|
|||||||
MessageTestCaseFail,
|
MessageTestCaseFail,
|
||||||
MessageTestCaseEnd,
|
MessageTestCaseEnd,
|
||||||
MessageTestCaseRepetition,
|
MessageTestCaseRepetition,
|
||||||
|
MessageIntermediate,
|
||||||
MessageCurrentTest, INTERNAL_MESSAGES_END = MessageCurrentTest,
|
MessageCurrentTest, INTERNAL_MESSAGES_END = MessageCurrentTest,
|
||||||
|
|
||||||
Invalid,
|
Invalid,
|
||||||
@@ -91,6 +92,10 @@ public:
|
|||||||
static QString resultToString(const Result::Type type);
|
static QString resultToString(const Result::Type type);
|
||||||
static QColor colorForType(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:
|
private:
|
||||||
QString m_name;
|
QString m_name;
|
||||||
Result::Type m_result = Result::Invalid;
|
Result::Type m_result = Result::Invalid;
|
||||||
|
@@ -31,7 +31,6 @@
|
|||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
#include <QAbstractItemView>
|
#include <QAbstractItemView>
|
||||||
#include <QDebug>
|
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QTextLayout>
|
#include <QTextLayout>
|
||||||
|
|
||||||
@@ -49,15 +48,13 @@ void TestResultDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
|
|||||||
{
|
{
|
||||||
QStyleOptionViewItem opt = option;
|
QStyleOptionViewItem opt = option;
|
||||||
initStyleOption(&opt, index);
|
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();
|
painter->save();
|
||||||
|
|
||||||
QFontMetrics fm(opt.font);
|
QFontMetrics fm(opt.font);
|
||||||
QColor foreground;
|
QColor foreground;
|
||||||
|
|
||||||
const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(opt.widget);
|
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) {
|
if (selected) {
|
||||||
painter->setBrush(opt.palette.highlight().color());
|
painter->setBrush(opt.palette.highlight().color());
|
||||||
@@ -75,11 +72,6 @@ void TestResultDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
|
|||||||
const TestResult *testResult = resultFilterModel->testResult(index);
|
const TestResult *testResult = resultFilterModel->testResult(index);
|
||||||
QTC_ASSERT(testResult, painter->restore();return);
|
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>();
|
QIcon icon = index.data(Qt::DecorationRole).value<QIcon>();
|
||||||
if (!icon.isNull())
|
if (!icon.isNull())
|
||||||
painter->drawPixmap(positions.left(), positions.top(),
|
painter->drawPixmap(positions.left(), positions.top(),
|
||||||
@@ -140,7 +132,6 @@ QSize TestResultDelegate::sizeHint(const QStyleOptionViewItem &option, const QMo
|
|||||||
QStyleOptionViewItem opt = option;
|
QStyleOptionViewItem opt = option;
|
||||||
// make sure opt.rect is initialized correctly - otherwise we might get a width of 0
|
// make sure opt.rect is initialized correctly - otherwise we might get a width of 0
|
||||||
opt.initFrom(opt.widget);
|
opt.initFrom(opt.widget);
|
||||||
initStyleOption(&opt, index);
|
|
||||||
|
|
||||||
const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(opt.widget);
|
const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(opt.widget);
|
||||||
const bool selected = view->selectionModel()->currentIndex() == index;
|
const bool selected = view->selectionModel()->currentIndex() == index;
|
||||||
|
@@ -67,7 +67,14 @@ private:
|
|||||||
m_typeAreaWidth = QFontMetrics(options.font).width("XXXXXXXX");
|
m_typeAreaWidth = QFontMetrics(options.font).width("XXXXXXXX");
|
||||||
m_indentation = options.widget ? options.widget->style()->pixelMetric(
|
m_indentation = options.widget ? options.widget->style()->pixelMetric(
|
||||||
QStyle::PM_TreeViewIndentation, &options) : 0;
|
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;
|
int flexibleArea = lineAreaLeft() - textAreaLeft() - ITEM_SPACING;
|
||||||
if (m_maxFileLength > flexibleArea / 2)
|
if (m_maxFileLength > flexibleArea / 2)
|
||||||
m_realFileLength = flexibleArea / 2;
|
m_realFileLength = flexibleArea / 2;
|
||||||
@@ -84,12 +91,10 @@ private:
|
|||||||
int fontHeight() const { return m_fontHeight; }
|
int fontHeight() const { return m_fontHeight; }
|
||||||
int typeAreaLeft() const { return left() + ICON_SIZE + ITEM_SPACING; }
|
int typeAreaLeft() const { return left() + ICON_SIZE + ITEM_SPACING; }
|
||||||
int typeAreaWidth() const { return m_typeAreaWidth; }
|
int typeAreaWidth() const { return m_typeAreaWidth; }
|
||||||
int textAreaLeft() const { return typeAreaLeft() + m_typeAreaWidth + ITEM_SPACING
|
int textAreaLeft() const { return typeAreaLeft() + m_typeAreaWidth + ITEM_SPACING; }
|
||||||
+ (1 - m_level) * m_indentation; }
|
|
||||||
int textAreaWidth() const { return fileAreaLeft() - ITEM_SPACING - textAreaLeft(); }
|
int textAreaWidth() const { return fileAreaLeft() - ITEM_SPACING - textAreaLeft(); }
|
||||||
int fileAreaLeft() const { return lineAreaLeft() - ITEM_SPACING - m_realFileLength; }
|
int fileAreaLeft() const { return lineAreaLeft() - ITEM_SPACING - m_realFileLength; }
|
||||||
int lineAreaLeft() const { return right() - m_maxLineLength; }
|
int lineAreaLeft() const { return right() - m_maxLineLength; }
|
||||||
int indentation() const { return m_indentation; }
|
|
||||||
|
|
||||||
QRect typeArea() const { return QRect(typeAreaLeft(), top(),
|
QRect typeArea() const { return QRect(typeAreaLeft(), top(),
|
||||||
typeAreaWidth(), m_fontHeight); }
|
typeAreaWidth(), m_fontHeight); }
|
||||||
|
@@ -99,7 +99,8 @@ void TestResultItem::updateDescription(const QString &description)
|
|||||||
|
|
||||||
void TestResultItem::updateResult()
|
void TestResultItem::updateResult()
|
||||||
{
|
{
|
||||||
if (m_testResult->result() != Result::MessageTestCaseStart)
|
if (m_testResult->result() != Result::MessageTestCaseStart
|
||||||
|
&& m_testResult->result() != Result::MessageIntermediate)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Result::Type newResult = Result::MessageTestCaseSuccess;
|
Result::Type newResult = Result::MessageTestCaseSuccess;
|
||||||
@@ -110,6 +111,7 @@ void TestResultItem::updateResult()
|
|||||||
case Result::Fail:
|
case Result::Fail:
|
||||||
case Result::MessageFatal:
|
case Result::MessageFatal:
|
||||||
case Result::UnexpectedPass:
|
case Result::UnexpectedPass:
|
||||||
|
case Result::MessageTestCaseFail:
|
||||||
m_testResult->setResult(Result::MessageTestCaseFail);
|
m_testResult->setResult(Result::MessageTestCaseFail);
|
||||||
return;
|
return;
|
||||||
case Result::ExpectedFail:
|
case Result::ExpectedFail:
|
||||||
@@ -117,6 +119,7 @@ void TestResultItem::updateResult()
|
|||||||
case Result::Skip:
|
case Result::Skip:
|
||||||
case Result::BlacklistedFail:
|
case Result::BlacklistedFail:
|
||||||
case Result::BlacklistedPass:
|
case Result::BlacklistedPass:
|
||||||
|
case Result::MessageTestCaseWarn:
|
||||||
newResult = Result::MessageTestCaseWarn;
|
newResult = Result::MessageTestCaseWarn;
|
||||||
break;
|
break;
|
||||||
default: {}
|
default: {}
|
||||||
@@ -126,6 +129,40 @@ void TestResultItem::updateResult()
|
|||||||
m_testResult->setResult(newResult);
|
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::TestResultModel(QObject *parent)
|
TestResultModel::TestResultModel(QObject *parent)
|
||||||
@@ -135,12 +172,11 @@ TestResultModel::TestResultModel(QObject *parent)
|
|||||||
|
|
||||||
void TestResultModel::addTestResult(const TestResultPtr &testResult, bool autoExpand)
|
void TestResultModel::addTestResult(const TestResultPtr &testResult, bool autoExpand)
|
||||||
{
|
{
|
||||||
const QVector<Utils::TreeItem *> &topLevelItems = rootItem()->children();
|
const int lastRow = rootItem()->childCount() - 1;
|
||||||
const int lastRow = topLevelItems.size() - 1;
|
|
||||||
if (testResult->result() == Result::MessageCurrentTest) {
|
if (testResult->result() == Result::MessageCurrentTest) {
|
||||||
// MessageCurrentTest should always be the last top level item
|
// MessageCurrentTest should always be the last top level item
|
||||||
if (lastRow >= 0) {
|
if (lastRow >= 0) {
|
||||||
TestResultItem *current = static_cast<TestResultItem *>(topLevelItems.at(lastRow));
|
TestResultItem *current = static_cast<TestResultItem *>(rootItem()->childAt(lastRow));
|
||||||
const TestResult *result = current->testResult();
|
const TestResult *result = current->testResult();
|
||||||
if (result && result->result() == Result::MessageCurrentTest) {
|
if (result && result->result() == Result::MessageCurrentTest) {
|
||||||
current->updateDescription(testResult->description());
|
current->updateDescription(testResult->description());
|
||||||
@@ -158,26 +194,20 @@ void TestResultModel::addTestResult(const TestResultPtr &testResult, bool autoEx
|
|||||||
m_testResultCount[testResult->result()]++;
|
m_testResultCount[testResult->result()]++;
|
||||||
|
|
||||||
TestResultItem *newItem = new TestResultItem(testResult);
|
TestResultItem *newItem = new TestResultItem(testResult);
|
||||||
// FIXME this might be totally wrong... we need some more unique information!
|
TestResultItem *parentItem = findParentItemFor(newItem);
|
||||||
if (!testResult->name().isEmpty()) {
|
addFileName(testResult->fileName()); // ensure we calculate the results pane correctly
|
||||||
for (int row = lastRow; row >= 0; --row) {
|
if (parentItem) {
|
||||||
TestResultItem *current = static_cast<TestResultItem *>(topLevelItems.at(row));
|
parentItem->appendChild(newItem);
|
||||||
const TestResult *result = current->testResult();
|
|
||||||
if (result && result->name() == testResult->name()) {
|
|
||||||
current->appendChild(newItem);
|
|
||||||
if (autoExpand)
|
if (autoExpand)
|
||||||
current->expand();
|
parentItem->expand();
|
||||||
if (testResult->result() == Result::MessageTestCaseEnd) {
|
if (testResult->result() == Result::MessageTestCaseEnd) {
|
||||||
current->updateResult();
|
parentItem->updateIntermediateChildren();
|
||||||
emit dataChanged(current->index(), current->index());
|
parentItem->updateResult();
|
||||||
|
emit dataChanged(parentItem->index(), parentItem->index());
|
||||||
}
|
}
|
||||||
return;
|
} else {
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if we have a MessageCurrentTest present, add the new top level item before it
|
|
||||||
if (lastRow >= 0) {
|
if (lastRow >= 0) {
|
||||||
TestResultItem *current = static_cast<TestResultItem *>(topLevelItems.at(lastRow));
|
TestResultItem *current = static_cast<TestResultItem *>(rootItem()->childAt(lastRow));
|
||||||
const TestResult *result = current->testResult();
|
const TestResult *result = current->testResult();
|
||||||
if (result && result->result() == Result::MessageCurrentTest) {
|
if (result && result->result() == Result::MessageCurrentTest) {
|
||||||
rootItem()->insertChild(current->index().row(), newItem);
|
rootItem()->insertChild(current->index().row(), newItem);
|
||||||
@@ -186,6 +216,7 @@ void TestResultModel::addTestResult(const TestResultPtr &testResult, bool autoEx
|
|||||||
}
|
}
|
||||||
// there is no MessageCurrentTest at the last row, but we have a toplevel item - just add it
|
// there is no MessageCurrentTest at the last row, but we have a toplevel item - just add it
|
||||||
rootItem()->appendChild(newItem);
|
rootItem()->appendChild(newItem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestResultModel::removeCurrentTestMessage()
|
void TestResultModel::removeCurrentTestMessage()
|
||||||
@@ -205,7 +236,7 @@ void TestResultModel::clearTestResults()
|
|||||||
clear();
|
clear();
|
||||||
m_testResultCount.clear();
|
m_testResultCount.clear();
|
||||||
m_disabled = 0;
|
m_disabled = 0;
|
||||||
m_processedIndices.clear();
|
m_fileNames.clear();
|
||||||
m_maxWidthOfFileName = 0;
|
m_maxWidthOfFileName = 0;
|
||||||
m_widthOfLineNumber = 0;
|
m_widthOfLineNumber = 0;
|
||||||
}
|
}
|
||||||
@@ -218,38 +249,28 @@ const TestResult *TestResultModel::testResult(const QModelIndex &idx)
|
|||||||
return 0;
|
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)
|
int TestResultModel::maxWidthOfFileName(const QFont &font)
|
||||||
{
|
{
|
||||||
if (font != m_measurementFont) {
|
if (font != m_measurementFont)
|
||||||
m_processedIndices.clear();
|
recalculateMaxWidthOfFileName(font);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return m_maxWidthOfFileName;
|
return m_maxWidthOfFileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,6 +284,44 @@ int TestResultModel::maxWidthOfLineNumber(const QFont &font)
|
|||||||
return m_widthOfLineNumber;
|
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 **********************************/
|
/********************************** Filter Model **********************************/
|
||||||
|
|
||||||
TestResultFilterModel::TestResultFilterModel(TestResultModel *sourceModel, QObject *parent)
|
TestResultFilterModel::TestResultFilterModel(TestResultModel *sourceModel, QObject *parent)
|
||||||
@@ -279,7 +338,7 @@ void TestResultFilterModel::enableAllResultTypes()
|
|||||||
<< Result::UnexpectedPass << Result::Skip << Result::MessageDebug
|
<< Result::UnexpectedPass << Result::Skip << Result::MessageDebug
|
||||||
<< Result::MessageWarn << Result::MessageInternal
|
<< Result::MessageWarn << Result::MessageInternal
|
||||||
<< Result::MessageFatal << Result::Invalid << Result::BlacklistedPass
|
<< Result::MessageFatal << Result::Invalid << Result::BlacklistedPass
|
||||||
<< Result::BlacklistedFail << Result::Benchmark
|
<< Result::BlacklistedFail << Result::Benchmark << Result::MessageIntermediate
|
||||||
<< Result::MessageCurrentTest << Result::MessageTestCaseStart
|
<< Result::MessageCurrentTest << Result::MessageTestCaseStart
|
||||||
<< Result::MessageTestCaseSuccess << Result::MessageTestCaseWarn
|
<< Result::MessageTestCaseSuccess << Result::MessageTestCaseWarn
|
||||||
<< Result::MessageTestCaseFail << Result::MessageTestCaseEnd
|
<< 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);
|
for (int row = 0, count = m_sourceModel->rowCount(srcIndex); row < count; ++row) {
|
||||||
QTC_ASSERT(item, return false);
|
const QModelIndex &child = srcIndex.child(row, 0);
|
||||||
|
Result::Type type = m_sourceModel->testResult(child)->result();
|
||||||
for (const Utils::TreeItem *child : item->children()) {
|
if (type == Result::MessageTestCaseSuccess)
|
||||||
const TestResultItem *resultItem = static_cast<const TestResultItem *>(child);
|
type = Result::Pass;
|
||||||
if (m_enabled.contains(resultItem->testResult()->result()))
|
if (type == Result::MessageTestCaseFail || type == Result::MessageTestCaseWarn) {
|
||||||
|
if (acceptTestCaseResult(child))
|
||||||
return true;
|
return true;
|
||||||
|
} else if (m_enabled.contains(type)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@@ -46,6 +46,10 @@ public:
|
|||||||
const TestResult *testResult() const { return m_testResult.data(); }
|
const TestResult *testResult() const { return m_testResult.data(); }
|
||||||
void updateDescription(const QString &description);
|
void updateDescription(const QString &description);
|
||||||
void updateResult();
|
void updateResult();
|
||||||
|
void updateIntermediateChildren();
|
||||||
|
|
||||||
|
TestResultItem *intermediateFor(const TestResultItem *item) const;
|
||||||
|
TestResultItem *createAndAddIntermediateFor(const TestResultItem *child);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TestResultPtr m_testResult;
|
TestResultPtr m_testResult;
|
||||||
@@ -69,11 +73,15 @@ public:
|
|||||||
int disabledTests() const { return m_disabled; }
|
int disabledTests() const { return m_disabled; }
|
||||||
|
|
||||||
private:
|
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;
|
QMap<Result::Type, int> m_testResultCount;
|
||||||
int m_widthOfLineNumber = 0;
|
int m_widthOfLineNumber = 0;
|
||||||
int m_maxWidthOfFileName = 0;
|
int m_maxWidthOfFileName = 0;
|
||||||
int m_disabled = 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;
|
QFont m_measurementFont;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -93,7 +101,7 @@ protected:
|
|||||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool acceptTestCaseResult(const QModelIndex &index) const;
|
bool acceptTestCaseResult(const QModelIndex &srcIndex) const;
|
||||||
TestResultModel *m_sourceModel;
|
TestResultModel *m_sourceModel;
|
||||||
QSet<Result::Type> m_enabled;
|
QSet<Result::Type> m_enabled;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user