forked from qt-creator/qt-creator
AutoTest: Provide way to access the original test output
When running tests the original output is processed and not presented to the user at all. For crashing tests this could mean that output was not able to get processed completely (e.g. when having XML as output and relying on well-formed code) Unhandled output could also lead to incorrect results. This patch adds another view to the results pane which contains the complete output of the last test run. Change-Id: I923496e9c440de4ea68bee55415777ea5c2379c2 Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -11,5 +11,9 @@
|
|||||||
<file>images/runselected_tickmarks.png</file>
|
<file>images/runselected_tickmarks.png</file>
|
||||||
<file>images/runselected_tickmarks@2x.png</file>
|
<file>images/runselected_tickmarks@2x.png</file>
|
||||||
<file>images/data.png</file>
|
<file>images/data.png</file>
|
||||||
|
<file>images/text.png</file>
|
||||||
|
<file>images/text@2x.png</file>
|
||||||
|
<file>images/visual.png</file>
|
||||||
|
<file>images/visual@2x.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ const Utils::Icon RESULT_MESSAGEWARN({
|
|||||||
const Utils::Icon RESULT_MESSAGEFATAL({
|
const Utils::Icon RESULT_MESSAGEFATAL({
|
||||||
{":/utils/images/filledcircle.png", Utils::Theme::OutputPanes_TestFatalTextColor}},
|
{":/utils/images/filledcircle.png", Utils::Theme::OutputPanes_TestFatalTextColor}},
|
||||||
Utils::Icon::Tint);
|
Utils::Icon::Tint);
|
||||||
|
const Utils::Icon VISUAL_DISPLAY({{":/images/visual.png", Utils::Theme::IconsBaseColor}});
|
||||||
|
const Utils::Icon TEXT_DISPLAY({{":/images/text.png", Utils::Theme::IconsBaseColor}});
|
||||||
|
|
||||||
} // namespace Icons
|
} // namespace Icons
|
||||||
} // namespace Autotest
|
} // namespace Autotest
|
||||||
|
|||||||
@@ -56,20 +56,7 @@ void GTestOutputReader::processOutput(const QByteArray &outputLine)
|
|||||||
static QRegExp errorLocation("^(.*)\\((\\d+)\\): error:.*$");
|
static QRegExp errorLocation("^(.*)\\((\\d+)\\): error:.*$");
|
||||||
static QRegExp iterations("^Repeating all tests \\(iteration (\\d+)\\) \\. \\. \\.$");
|
static QRegExp iterations("^Repeating all tests \\(iteration (\\d+)\\) \\. \\. \\.$");
|
||||||
|
|
||||||
QByteArray read = outputLine;
|
const QString line = QString::fromLatin1(outputLine);
|
||||||
if (!m_unprocessed.isEmpty()) {
|
|
||||||
read = m_unprocessed + read;
|
|
||||||
m_unprocessed.clear();
|
|
||||||
}
|
|
||||||
if (!read.endsWith('\n')) {
|
|
||||||
m_unprocessed = read;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
read.chop(1); // remove the newline from the output
|
|
||||||
if (read.endsWith('\r'))
|
|
||||||
read.chop(1);
|
|
||||||
|
|
||||||
const QString line = QString::fromLatin1(read);
|
|
||||||
if (line.trimmed().isEmpty())
|
if (line.trimmed().isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ private:
|
|||||||
QString m_currentTestName;
|
QString m_currentTestName;
|
||||||
QString m_currentTestSet;
|
QString m_currentTestSet;
|
||||||
QString m_description;
|
QString m_description;
|
||||||
QByteArray m_unprocessed;
|
|
||||||
int m_iteration = 1;
|
int m_iteration = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
BIN
src/plugins/autotest/images/text.png
Normal file
BIN
src/plugins/autotest/images/text.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 148 B |
BIN
src/plugins/autotest/images/text@2x.png
Normal file
BIN
src/plugins/autotest/images/text@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 274 B |
BIN
src/plugins/autotest/images/visual.png
Normal file
BIN
src/plugins/autotest/images/visual.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 213 B |
BIN
src/plugins/autotest/images/visual@2x.png
Normal file
BIN
src/plugins/autotest/images/visual@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 297 B |
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include "testoutputreader.h"
|
#include "testoutputreader.h"
|
||||||
#include "testresult.h"
|
#include "testresult.h"
|
||||||
|
#include "testresultspane.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
@@ -41,12 +42,21 @@ TestOutputReader::TestOutputReader(const QFutureInterface<TestResultPtr> &future
|
|||||||
if (m_testApplication) {
|
if (m_testApplication) {
|
||||||
connect(m_testApplication, &QProcess::readyRead,
|
connect(m_testApplication, &QProcess::readyRead,
|
||||||
this, [this] () {
|
this, [this] () {
|
||||||
while (m_testApplication->canReadLine())
|
while (m_testApplication->canReadLine()) {
|
||||||
processOutput(m_testApplication->readLine());
|
QByteArray output = m_testApplication->readLine();
|
||||||
|
output.chop(1); // remove the newline from the output
|
||||||
|
if (output.endsWith('\r'))
|
||||||
|
output.chop(1);
|
||||||
|
|
||||||
|
emit newOutputAvailable(output);
|
||||||
|
processOutput(output);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
connect(m_testApplication, &QProcess::readyReadStandardError,
|
connect(m_testApplication, &QProcess::readyReadStandardError,
|
||||||
this, [this] () {
|
this, [this] () {
|
||||||
processStdError(m_testApplication->readAllStandardError());
|
const QByteArray output = m_testApplication->readAllStandardError();
|
||||||
|
emit newOutputAvailable(output);
|
||||||
|
processStdError(output);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ public:
|
|||||||
virtual void processOutput(const QByteArray &outputLine) = 0;
|
virtual void processOutput(const QByteArray &outputLine) = 0;
|
||||||
virtual void processStdError(const QByteArray &output);
|
virtual void processStdError(const QByteArray &output);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void newOutputAvailable(const QByteArray &output);
|
||||||
protected:
|
protected:
|
||||||
QFutureInterface<TestResultPtr> m_futureInterface;
|
QFutureInterface<TestResultPtr> m_futureInterface;
|
||||||
QProcess *m_testApplication; // not owned
|
QProcess *m_testApplication; // not owned
|
||||||
|
|||||||
@@ -33,16 +33,15 @@
|
|||||||
#include "testtreemodel.h"
|
#include "testtreemodel.h"
|
||||||
#include "testcodeparser.h"
|
#include "testcodeparser.h"
|
||||||
|
|
||||||
|
#include <aggregation/aggregate.h>
|
||||||
#include <coreplugin/coreconstants.h>
|
#include <coreplugin/coreconstants.h>
|
||||||
#include <coreplugin/editormanager/editormanager.h>
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
|
#include <coreplugin/find/basetextfind.h>
|
||||||
#include <coreplugin/find/itemviewfind.h>
|
#include <coreplugin/find/itemviewfind.h>
|
||||||
#include <coreplugin/icontext.h>
|
#include <coreplugin/icontext.h>
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
#include <projectexplorer/projectexplorer.h>
|
#include <projectexplorer/projectexplorer.h>
|
||||||
|
|
||||||
#include <texteditor/texteditor.h>
|
#include <texteditor/texteditor.h>
|
||||||
|
|
||||||
#include <utils/theme/theme.h>
|
#include <utils/theme/theme.h>
|
||||||
#include <utils/utilsicons.h>
|
#include <utils/utilsicons.h>
|
||||||
|
|
||||||
@@ -54,6 +53,7 @@
|
|||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
|
#include <QStackedWidget>
|
||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
@@ -79,11 +79,13 @@ TestResultsPane::TestResultsPane(QObject *parent) :
|
|||||||
Core::IOutputPane(parent),
|
Core::IOutputPane(parent),
|
||||||
m_context(new Core::IContext(this))
|
m_context(new Core::IContext(this))
|
||||||
{
|
{
|
||||||
m_outputWidget = new QWidget;
|
m_outputWidget = new QStackedWidget;
|
||||||
|
QWidget *visualOutputWidget = new QWidget;
|
||||||
|
m_outputWidget->addWidget(visualOutputWidget);
|
||||||
QVBoxLayout *outputLayout = new QVBoxLayout;
|
QVBoxLayout *outputLayout = new QVBoxLayout;
|
||||||
outputLayout->setMargin(0);
|
outputLayout->setMargin(0);
|
||||||
outputLayout->setSpacing(0);
|
outputLayout->setSpacing(0);
|
||||||
m_outputWidget->setLayout(outputLayout);
|
visualOutputWidget->setLayout(outputLayout);
|
||||||
|
|
||||||
QPalette pal;
|
QPalette pal;
|
||||||
pal.setColor(QPalette::Window,
|
pal.setColor(QPalette::Window,
|
||||||
@@ -103,7 +105,7 @@ TestResultsPane::TestResultsPane(QObject *parent) :
|
|||||||
|
|
||||||
outputLayout->addWidget(m_summaryWidget);
|
outputLayout->addWidget(m_summaryWidget);
|
||||||
|
|
||||||
m_treeView = new ResultsTreeView(m_outputWidget);
|
m_treeView = new ResultsTreeView(visualOutputWidget);
|
||||||
m_treeView->setHeaderHidden(true);
|
m_treeView->setHeaderHidden(true);
|
||||||
m_treeView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
m_treeView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||||
m_treeView->setContextMenuPolicy(Qt::CustomContextMenu);
|
m_treeView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
@@ -119,6 +121,19 @@ TestResultsPane::TestResultsPane(QObject *parent) :
|
|||||||
|
|
||||||
outputLayout->addWidget(Core::ItemViewFind::createSearchableWrapper(m_treeView));
|
outputLayout->addWidget(Core::ItemViewFind::createSearchableWrapper(m_treeView));
|
||||||
|
|
||||||
|
m_textOutput = new QPlainTextEdit;
|
||||||
|
m_textOutput->setPalette(pal);
|
||||||
|
QFont font("monospace");
|
||||||
|
font.setStyleHint(QFont::TypeWriter);
|
||||||
|
m_textOutput->setFont(font);
|
||||||
|
m_textOutput->setWordWrapMode(QTextOption::WordWrap);
|
||||||
|
m_textOutput->setReadOnly(true);
|
||||||
|
m_outputWidget->addWidget(m_textOutput);
|
||||||
|
|
||||||
|
auto agg = new Aggregation::Aggregate;
|
||||||
|
agg->add(m_textOutput);
|
||||||
|
agg->add(new Core::BaseTextFind(m_textOutput));
|
||||||
|
|
||||||
createToolButtons();
|
createToolButtons();
|
||||||
|
|
||||||
connect(m_treeView, &Utils::TreeView::activated, this, &TestResultsPane::onItemActivated);
|
connect(m_treeView, &Utils::TreeView::activated, this, &TestResultsPane::onItemActivated);
|
||||||
@@ -188,6 +203,11 @@ void TestResultsPane::createToolButtons()
|
|||||||
initializeFilterMenu();
|
initializeFilterMenu();
|
||||||
connect(m_filterMenu, &QMenu::triggered, this, &TestResultsPane::filterMenuTriggered);
|
connect(m_filterMenu, &QMenu::triggered, this, &TestResultsPane::filterMenuTriggered);
|
||||||
m_filterButton->setMenu(m_filterMenu);
|
m_filterButton->setMenu(m_filterMenu);
|
||||||
|
m_outputToggleButton = new QToolButton(m_treeView);
|
||||||
|
m_outputToggleButton->setIcon(Icons::TEXT_DISPLAY.icon());
|
||||||
|
m_outputToggleButton->setToolTip(tr("Switch Between Visual and Text Display"));
|
||||||
|
m_outputToggleButton->setEnabled(true);
|
||||||
|
connect(m_outputToggleButton, &QToolButton::clicked, this, &TestResultsPane::toggleOutputStyle);
|
||||||
}
|
}
|
||||||
|
|
||||||
static TestResultsPane *s_instance = nullptr;
|
static TestResultsPane *s_instance = nullptr;
|
||||||
@@ -202,6 +222,8 @@ TestResultsPane *TestResultsPane::instance()
|
|||||||
TestResultsPane::~TestResultsPane()
|
TestResultsPane::~TestResultsPane()
|
||||||
{
|
{
|
||||||
delete m_treeView;
|
delete m_treeView;
|
||||||
|
if (!m_outputWidget->parent())
|
||||||
|
delete m_outputWidget;
|
||||||
s_instance = nullptr;
|
s_instance = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,6 +239,11 @@ void TestResultsPane::addTestResult(const TestResultPtr &result)
|
|||||||
navigateStateChanged();
|
navigateStateChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestResultsPane::addOutput(const QByteArray &output)
|
||||||
|
{
|
||||||
|
m_textOutput->appendPlainText(QString::fromLatin1(output));
|
||||||
|
}
|
||||||
|
|
||||||
QWidget *TestResultsPane::outputWidget(QWidget *parent)
|
QWidget *TestResultsPane::outputWidget(QWidget *parent)
|
||||||
{
|
{
|
||||||
if (m_outputWidget) {
|
if (m_outputWidget) {
|
||||||
@@ -229,7 +256,8 @@ QWidget *TestResultsPane::outputWidget(QWidget *parent)
|
|||||||
|
|
||||||
QList<QWidget *> TestResultsPane::toolBarWidgets() const
|
QList<QWidget *> TestResultsPane::toolBarWidgets() const
|
||||||
{
|
{
|
||||||
return {m_expandCollapse, m_runAll, m_runSelected, m_stopTestRun, m_filterButton};
|
return {m_expandCollapse, m_runAll, m_runSelected, m_stopTestRun, m_outputToggleButton,
|
||||||
|
m_filterButton};
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TestResultsPane::displayName() const
|
QString TestResultsPane::displayName() const
|
||||||
@@ -251,6 +279,7 @@ void TestResultsPane::clearContents()
|
|||||||
m_autoScroll = AutotestPlugin::instance()->settings()->autoScroll;
|
m_autoScroll = AutotestPlugin::instance()->settings()->autoScroll;
|
||||||
connect(m_treeView->verticalScrollBar(), &QScrollBar::rangeChanged,
|
connect(m_treeView->verticalScrollBar(), &QScrollBar::rangeChanged,
|
||||||
this, &TestResultsPane::onScrollBarRangeChanged, Qt::UniqueConnection);
|
this, &TestResultsPane::onScrollBarRangeChanged, Qt::UniqueConnection);
|
||||||
|
m_textOutput->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestResultsPane::visibilityChanged(bool visible)
|
void TestResultsPane::visibilityChanged(bool visible)
|
||||||
@@ -574,6 +603,14 @@ void TestResultsPane::onSaveWholeTriggered()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestResultsPane::toggleOutputStyle()
|
||||||
|
{
|
||||||
|
const bool displayText = m_outputWidget->currentIndex() == 0;
|
||||||
|
m_outputWidget->setCurrentIndex(displayText ? 1 : 0);
|
||||||
|
m_outputToggleButton->setIcon(displayText ? Icons::VISUAL_DISPLAY.icon()
|
||||||
|
: Icons::TEXT_DISPLAY.icon());
|
||||||
|
}
|
||||||
|
|
||||||
// helper for onCopyWholeTriggered() and onSaveWholeTriggered()
|
// helper for onCopyWholeTriggered() and onSaveWholeTriggered()
|
||||||
QString TestResultsPane::getWholeOutput(const QModelIndex &parent)
|
QString TestResultsPane::getWholeOutput(const QModelIndex &parent)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ class QKeyEvent;
|
|||||||
class QLabel;
|
class QLabel;
|
||||||
class QModelIndex;
|
class QModelIndex;
|
||||||
class QMenu;
|
class QMenu;
|
||||||
|
class QPlainTextEdit;
|
||||||
|
class QStackedWidget;
|
||||||
class QToolButton;
|
class QToolButton;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
@@ -88,6 +90,7 @@ public:
|
|||||||
void goToPrev() override;
|
void goToPrev() override;
|
||||||
|
|
||||||
void addTestResult(const TestResultPtr &result);
|
void addTestResult(const TestResultPtr &result);
|
||||||
|
void addOutput(const QByteArray &output);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit TestResultsPane(QObject *parent = 0);
|
explicit TestResultsPane(QObject *parent = 0);
|
||||||
@@ -109,9 +112,10 @@ private:
|
|||||||
void onCopyItemTriggered(const QModelIndex &idx);
|
void onCopyItemTriggered(const QModelIndex &idx);
|
||||||
void onCopyWholeTriggered();
|
void onCopyWholeTriggered();
|
||||||
void onSaveWholeTriggered();
|
void onSaveWholeTriggered();
|
||||||
|
void toggleOutputStyle();
|
||||||
QString getWholeOutput(const QModelIndex &parent = QModelIndex());
|
QString getWholeOutput(const QModelIndex &parent = QModelIndex());
|
||||||
|
|
||||||
QWidget *m_outputWidget;
|
QStackedWidget *m_outputWidget;
|
||||||
QFrame *m_summaryWidget;
|
QFrame *m_summaryWidget;
|
||||||
QLabel *m_summaryLabel;
|
QLabel *m_summaryLabel;
|
||||||
ResultsTreeView *m_treeView;
|
ResultsTreeView *m_treeView;
|
||||||
@@ -123,6 +127,8 @@ private:
|
|||||||
QToolButton *m_runSelected;
|
QToolButton *m_runSelected;
|
||||||
QToolButton *m_stopTestRun;
|
QToolButton *m_stopTestRun;
|
||||||
QToolButton *m_filterButton;
|
QToolButton *m_filterButton;
|
||||||
|
QToolButton *m_outputToggleButton;
|
||||||
|
QPlainTextEdit *m_textOutput;
|
||||||
QMenu *m_filterMenu;
|
QMenu *m_filterMenu;
|
||||||
bool m_wasVisibleBefore = false;
|
bool m_wasVisibleBefore = false;
|
||||||
bool m_autoScroll = false;
|
bool m_autoScroll = false;
|
||||||
|
|||||||
@@ -157,6 +157,8 @@ static void performTestRun(QFutureInterface<TestResultPtr> &futureInterface,
|
|||||||
QScopedPointer<TestOutputReader> outputReader;
|
QScopedPointer<TestOutputReader> outputReader;
|
||||||
outputReader.reset(testConfiguration->outputReader(futureInterface, &testProcess));
|
outputReader.reset(testConfiguration->outputReader(futureInterface, &testProcess));
|
||||||
QTC_ASSERT(outputReader, continue);
|
QTC_ASSERT(outputReader, continue);
|
||||||
|
TestRunner::connect(outputReader.data(), &TestOutputReader::newOutputAvailable,
|
||||||
|
TestResultsPane::instance(), &TestResultsPane::addOutput);
|
||||||
if (futureInterface.isCanceled())
|
if (futureInterface.isCanceled())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -364,7 +366,8 @@ void TestRunner::debugTests()
|
|||||||
|
|
||||||
if (useOutputProcessor) {
|
if (useOutputProcessor) {
|
||||||
TestOutputReader *outputreader = config->outputReader(*futureInterface, 0);
|
TestOutputReader *outputreader = config->outputReader(*futureInterface, 0);
|
||||||
|
connect(outputreader, &TestOutputReader::newOutputAvailable,
|
||||||
|
TestResultsPane::instance(), &TestResultsPane::addOutput);
|
||||||
connect(runControl, &ProjectExplorer::RunControl::appendMessageRequested,
|
connect(runControl, &ProjectExplorer::RunControl::appendMessageRequested,
|
||||||
this, [this, outputreader]
|
this, [this, outputreader]
|
||||||
(ProjectExplorer::RunControl *, const QString &msg, Utils::OutputFormat format) {
|
(ProjectExplorer::RunControl *, const QString &msg, Utils::OutputFormat format) {
|
||||||
|
|||||||
Reference in New Issue
Block a user