forked from qt-creator/qt-creator
AutoTest: Display results duration if possible
Some test frameworks provide duration information and we gather and display them rather hidden. Display duration of the test execution more prominent if it is available and the user has not turned off feature. Task-number: QTCREATORBUG-31242 Change-Id: I27641a5f811299e5876cce9e4739078fe3c2ae19 Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -28,12 +28,12 @@ GTestOutputReader::GTestOutputReader(Process *testApplication,
|
||||
void GTestOutputReader::processOutputLine(const QByteArray &outputLine)
|
||||
{
|
||||
static const QRegularExpression newTestStarts("^\\[-{10}\\] \\d+ tests? from (.*)$");
|
||||
static const QRegularExpression testEnds("^\\[-{10}\\] \\d+ tests? from (.*) \\((.*)\\)$");
|
||||
static const QRegularExpression testEnds("^\\[-{10}\\] \\d+ tests? from (.*) \\(((\\d+) .*)\\)$");
|
||||
static const QRegularExpression newTestSetStarts("^\\[ RUN \\] (.*)$");
|
||||
static const QRegularExpression testSetSuccess("^\\[ OK \\] (.*) \\((.*)\\)$");
|
||||
static const QRegularExpression testSetFail("^\\[ FAILED \\] (.*) \\((\\d+ ms)\\)$");
|
||||
static const QRegularExpression testSetFail("^\\[ FAILED \\] (.*) \\(((\\d+) ms)\\)$");
|
||||
static const QRegularExpression testDeath("^\\[ DEATH \\] (.*)$");
|
||||
static const QRegularExpression testSetSkipped("^\\[ SKIPPED \\] (.*) \\((\\d+ ms)\\)$");
|
||||
static const QRegularExpression testSetSkipped("^\\[ SKIPPED \\] (.*) \\(((\\d+) ms)\\)$");
|
||||
static const QRegularExpression disabledTests("^ YOU HAVE (\\d+) DISABLED TESTS?$");
|
||||
static const QRegularExpression iterations("^Repeating all tests "
|
||||
"\\(iteration (\\d+)\\) \\. \\. \\.$");
|
||||
@@ -70,6 +70,7 @@ void GTestOutputReader::processOutputLine(const QByteArray &outputLine)
|
||||
TestResult testResult = createDefaultResult();
|
||||
testResult.setResult(ResultType::TestEnd);
|
||||
testResult.setDescription(Tr::tr("Test execution took %1.").arg(match.captured(2)));
|
||||
testResult.setDuration(match.captured(3));
|
||||
reportResult(testResult);
|
||||
m_currentTestSuite.clear();
|
||||
m_currentTestCase.clear();
|
||||
@@ -102,6 +103,7 @@ void GTestOutputReader::processOutputLine(const QByteArray &outputLine)
|
||||
testResult = createDefaultResult();
|
||||
testResult.setResult(ResultType::MessageInternal);
|
||||
testResult.setDescription(Tr::tr("Execution took %1.").arg(match.captured(2)));
|
||||
testResult.setDuration(match.captured(3));
|
||||
reportResult(testResult);
|
||||
// TODO: bump progress?
|
||||
} else if (ExactMatch match = testSetFail.match(line)) {
|
||||
@@ -127,6 +129,7 @@ void GTestOutputReader::processOutputLine(const QByteArray &outputLine)
|
||||
testResult = createDefaultResult();
|
||||
testResult.setResult(ResultType::MessageInternal);
|
||||
testResult.setDescription(Tr::tr("Execution took %1.").arg(match.captured(2)));
|
||||
testResult.setDuration(match.captured(3));
|
||||
reportResult(testResult);
|
||||
} else if (ExactMatch match = logging.match(line)) {
|
||||
const QString severity = match.captured(1).trimmed();
|
||||
|
@@ -475,6 +475,8 @@ void QtTestOutputReader::sendCompleteInformation()
|
||||
}
|
||||
}
|
||||
testResult.setDescription(m_description);
|
||||
if (!m_duration.isEmpty())
|
||||
testResult.setDuration(m_duration);
|
||||
reportResult(testResult);
|
||||
}
|
||||
|
||||
@@ -510,6 +512,7 @@ void QtTestOutputReader::sendFinishMessage(bool isFunction)
|
||||
if (!m_duration.isEmpty()) {
|
||||
result.setDescription(isFunction ? Tr::tr("Execution took %1 ms.").arg(m_duration)
|
||||
: Tr::tr("Test execution took %1 ms.").arg(m_duration));
|
||||
result.setDuration(m_duration);
|
||||
} else {
|
||||
result.setDescription(isFunction ? Tr::tr("Test function finished.")
|
||||
: Tr::tr("Test finished."));
|
||||
|
@@ -96,11 +96,13 @@ public:
|
||||
QString description() const { return m_description; }
|
||||
Utils::FilePath fileName() const { return m_file; }
|
||||
int line() const { return m_line; }
|
||||
std::optional<QString> duration() const { return m_duration; }
|
||||
QVariant extraData() const { return m_hooks.extraData; }
|
||||
|
||||
void setDescription(const QString &description) { m_description = description; }
|
||||
void setFileName(const Utils::FilePath &fileName) { m_file = fileName; }
|
||||
void setLine(int line) { m_line = line; }
|
||||
void setDuration(const QString &milliSeconds) { m_duration.emplace(milliSeconds); }
|
||||
void setResult(ResultType type) { m_result = type; }
|
||||
|
||||
static ResultType resultFromString(const QString &resultString);
|
||||
@@ -114,6 +116,7 @@ public:
|
||||
|
||||
private:
|
||||
std::optional<QString> m_id = {};
|
||||
std::optional<QString> m_duration;
|
||||
QString m_name;
|
||||
ResultType m_result = ResultType::Invalid; // the real result..
|
||||
QString m_description;
|
||||
|
@@ -49,7 +49,7 @@ void TestResultDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
|
||||
painter->fillRect(opt.rect, background);
|
||||
painter->setPen(foreground);
|
||||
|
||||
const LayoutPositions positions(opt, resultFilterModel);
|
||||
const LayoutPositions positions(opt, resultFilterModel, m_showDuration);
|
||||
const TestResult testResult = resultFilterModel->testResult(index);
|
||||
QTC_ASSERT(testResult.isValid(), painter->restore(); return);
|
||||
|
||||
@@ -90,6 +90,16 @@ void TestResultDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
|
||||
fm.elidedText(output.left(2000), Qt::ElideRight, positions.textAreaWidth()));
|
||||
}
|
||||
|
||||
if (testResult.result() == ResultType::TestStart && m_showDuration && testResult.duration()) {
|
||||
const QString txt = testResult.duration().value() + " ms";
|
||||
QPen tmp = painter->pen();
|
||||
painter->setPen(opt.palette.mid().color());
|
||||
painter->setClipRect(positions.durationArea());
|
||||
option.widget->style()->drawItemText(painter, positions.durationArea(), Qt::AlignRight,
|
||||
opt.palette, true, txt);
|
||||
painter->setPen(tmp);
|
||||
}
|
||||
|
||||
const QString file = testResult.fileName().fileName();
|
||||
painter->setClipRect(positions.fileArea());
|
||||
painter->drawText(positions.fileAreaLeft(), positions.top() + fm.ascent(), file);
|
||||
@@ -119,7 +129,7 @@ QSize TestResultDelegate::sizeHint(const QStyleOptionViewItem &option, const QMo
|
||||
QFontMetrics fm(opt.font);
|
||||
int fontHeight = fm.height();
|
||||
TestResultFilterModel *resultFilterModel = static_cast<TestResultFilterModel *>(view->model());
|
||||
LayoutPositions positions(opt, resultFilterModel);
|
||||
LayoutPositions positions(opt, resultFilterModel, m_showDuration);
|
||||
const int depth = resultFilterModel->itemForIndex(index)->level() + 1;
|
||||
const int indentation = depth * view->style()->pixelMetric(QStyle::PM_TreeViewIndentation, &opt);
|
||||
|
||||
|
@@ -21,6 +21,7 @@ public:
|
||||
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
void currentChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
void clearCache();
|
||||
void setShowDuration(bool showDuration) { m_showDuration = showDuration; }
|
||||
|
||||
private:
|
||||
void limitTextOutput(QString &output) const;
|
||||
@@ -32,43 +33,57 @@ private:
|
||||
mutable QTextLayout m_lastCalculatedLayout;
|
||||
mutable int m_lastCalculatedHeight = 0;
|
||||
mutable int m_lastWidth = -1;
|
||||
bool m_showDuration = true;
|
||||
|
||||
class LayoutPositions
|
||||
{
|
||||
public:
|
||||
LayoutPositions(QStyleOptionViewItem &options, const TestResultFilterModel *filterModel)
|
||||
LayoutPositions(QStyleOptionViewItem &options, const TestResultFilterModel *filterModel,
|
||||
bool showDuration)
|
||||
: m_top(options.rect.top()),
|
||||
m_left(options.rect.left()),
|
||||
m_right(options.rect.right())
|
||||
m_right(options.rect.right()),
|
||||
m_showDuration(showDuration)
|
||||
{
|
||||
TestResultModel *srcModel = static_cast<TestResultModel *>(filterModel->sourceModel());
|
||||
m_maxFileLength = srcModel->maxWidthOfFileName(options.font);
|
||||
m_maxLineLength = srcModel->maxWidthOfLineNumber(options.font);
|
||||
m_realFileLength = m_maxFileLength;
|
||||
m_typeAreaWidth = QFontMetrics(options.font).horizontalAdvance("XXXXXXXX");
|
||||
m_durationAreaWidth = QFontMetrics(options.font).horizontalAdvance("XXXXXXXX ms");
|
||||
|
||||
int flexibleArea = lineAreaLeft() - textAreaLeft() - ITEM_SPACING;
|
||||
int flexibleArea = (m_showDuration ? durationAreaLeft() : fileAreaLeft())
|
||||
- textAreaLeft() - ItemSpacing;
|
||||
if (m_maxFileLength > flexibleArea / 2)
|
||||
m_realFileLength = flexibleArea / 2;
|
||||
m_fontHeight = QFontMetrics(options.font).height();
|
||||
}
|
||||
|
||||
int top() const { return m_top + ITEM_MARGIN; }
|
||||
int left() const { return m_left + ITEM_MARGIN; }
|
||||
int right() const { return m_right - ITEM_MARGIN; }
|
||||
int minimumHeight() const { return ICON_SIZE + 2 * ITEM_MARGIN; }
|
||||
int top() const { return m_top + ItemMargin; }
|
||||
int left() const { return m_left + ItemMargin; }
|
||||
int right() const { return m_right - ItemMargin; }
|
||||
int minimumHeight() const { return IconSize + 2 * ItemMargin; }
|
||||
|
||||
int iconSize() const { return ICON_SIZE; }
|
||||
int typeAreaLeft() const { return left() + ICON_SIZE + ITEM_SPACING; }
|
||||
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 iconSize() const { return IconSize; }
|
||||
int typeAreaLeft() const { return left() + IconSize + ItemSpacing; }
|
||||
int textAreaLeft() const { return typeAreaLeft() + m_typeAreaWidth + ItemSpacing; }
|
||||
int textAreaWidth() const
|
||||
{
|
||||
if (m_showDuration)
|
||||
return durationAreaLeft() - 3 * ItemSpacing - textAreaLeft();
|
||||
return fileAreaLeft() - ItemSpacing - textAreaLeft();
|
||||
}
|
||||
int durationAreaLeft() const { return fileAreaLeft() - 3 * ItemSpacing - m_durationAreaWidth; }
|
||||
int durationAreaWidth() const { return m_durationAreaWidth; }
|
||||
int fileAreaLeft() const { return lineAreaLeft() - ItemSpacing - m_realFileLength; }
|
||||
int lineAreaLeft() const { return right() - m_maxLineLength; }
|
||||
|
||||
QRect textArea() const { return QRect(textAreaLeft(), top(),
|
||||
textAreaWidth(), m_fontHeight); }
|
||||
QRect durationArea() const { return QRect(durationAreaLeft(), top(),
|
||||
durationAreaWidth(), m_fontHeight); }
|
||||
QRect fileArea() const { return QRect(fileAreaLeft(), top(),
|
||||
m_realFileLength + ITEM_SPACING, m_fontHeight); }
|
||||
m_realFileLength + ItemSpacing, m_fontHeight); }
|
||||
|
||||
QRect lineArea() const { return QRect(lineAreaLeft(), top(),
|
||||
m_maxLineLength, m_fontHeight); }
|
||||
@@ -82,10 +97,12 @@ private:
|
||||
int m_right;
|
||||
int m_fontHeight;
|
||||
int m_typeAreaWidth;
|
||||
int m_durationAreaWidth;
|
||||
bool m_showDuration;
|
||||
|
||||
static const int ICON_SIZE = 16;
|
||||
static const int ITEM_MARGIN = 2;
|
||||
static const int ITEM_SPACING = 4;
|
||||
static constexpr int IconSize = 16;
|
||||
static constexpr int ItemMargin = 2;
|
||||
static constexpr int ItemSpacing = 4;
|
||||
|
||||
};
|
||||
};
|
||||
|
@@ -117,12 +117,16 @@ static bool isSignificant(ResultType type)
|
||||
}
|
||||
|
||||
void TestResultItem::updateResult(bool &changed, ResultType addedChildType,
|
||||
const std::optional<SummaryEvaluation> &summary)
|
||||
const std::optional<SummaryEvaluation> &summary,
|
||||
const std::optional<QString> duration)
|
||||
{
|
||||
changed = false;
|
||||
if (m_testResult.result() != ResultType::TestStart)
|
||||
return;
|
||||
|
||||
if (addedChildType == ResultType::TestEnd && duration)
|
||||
m_testResult.setDuration(*duration);
|
||||
|
||||
if (!isSignificant(addedChildType) || (addedChildType == ResultType::TestStart && !summary))
|
||||
return;
|
||||
|
||||
@@ -244,7 +248,8 @@ void TestResultModel::updateParent(const TestResultItem *item)
|
||||
if (parentItem == rootItem()) // do not update invisible root item
|
||||
return;
|
||||
bool changed = false;
|
||||
parentItem->updateResult(changed, item->testResult().result(), item->summaryResult());
|
||||
parentItem->updateResult(changed, item->testResult().result(), item->summaryResult(),
|
||||
item->testResult().duration());
|
||||
bool changedType = parentItem->updateDescendantTypes(item->testResult().result());
|
||||
if (!changed && !changedType)
|
||||
return;
|
||||
|
@@ -37,7 +37,8 @@ public:
|
||||
};
|
||||
|
||||
void updateResult(bool &changed, ResultType addedChildType,
|
||||
const std::optional<SummaryEvaluation> &summary);
|
||||
const std::optional<SummaryEvaluation> &summary,
|
||||
const std::optional<QString> duration);
|
||||
|
||||
TestResultItem *intermediateFor(const TestResultItem *item) const;
|
||||
TestResultItem *createAndAddIntermediateFor(const TestResultItem *child);
|
||||
|
@@ -213,6 +213,22 @@ void TestResultsPane::createToolButtons()
|
||||
m_outputToggleButton->setToolTip(Tr::tr("Switch Between Visual and Text Display"));
|
||||
m_outputToggleButton->setEnabled(true);
|
||||
connect(m_outputToggleButton, &QToolButton::clicked, this, &TestResultsPane::toggleOutputStyle);
|
||||
m_showDurationButton = new QToolButton(m_treeView);
|
||||
auto icon = Utils::Icon({{":/utils/images/stopwatch.png", Utils::Theme::IconsBaseColor}});
|
||||
m_showDurationButton->setIcon(icon.icon());
|
||||
m_showDurationButton->setToolTip(Tr::tr("Show Durations"));
|
||||
m_showDurationButton->setCheckable(true);
|
||||
m_showDurationButton->setChecked(true);
|
||||
connect(m_showDurationButton, &QToolButton::toggled, this, [this](bool checked) {
|
||||
if (auto trd = qobject_cast<TestResultDelegate *>(m_treeView->itemDelegate())) {
|
||||
trd->setShowDuration(checked);
|
||||
if (auto rowCount = m_model->rowCount()) {
|
||||
QModelIndex tl = m_model->index(0, 0);
|
||||
QModelIndex br = m_model->index(rowCount - 1, 0);
|
||||
emit m_model->dataChanged(tl, br, {Qt::DisplayRole});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static TestResultsPane *s_instance = nullptr;
|
||||
@@ -272,7 +288,8 @@ QWidget *TestResultsPane::outputWidget(QWidget *parent)
|
||||
QList<QWidget *> TestResultsPane::toolBarWidgets() const
|
||||
{
|
||||
QList<QWidget *> result = {m_expandCollapse, m_runAll, m_runSelected, m_runFailed,
|
||||
m_runFile, m_stopTestRun, m_outputToggleButton, m_filterButton};
|
||||
m_runFile, m_stopTestRun, m_showDurationButton,
|
||||
m_outputToggleButton, m_filterButton};
|
||||
for (QWidget *widget : IOutputPane::toolBarWidgets())
|
||||
result.append(widget);
|
||||
return result;
|
||||
|
@@ -115,6 +115,7 @@ private:
|
||||
QToolButton *m_stopTestRun;
|
||||
QToolButton *m_filterButton;
|
||||
QToolButton *m_outputToggleButton;
|
||||
QToolButton *m_showDurationButton;
|
||||
Core::OutputWindow *m_textOutput;
|
||||
QMenu *m_filterMenu;
|
||||
bool m_autoScroll = false;
|
||||
|
Reference in New Issue
Block a user