diff --git a/src/plugins/autotest/autotest.qrc b/src/plugins/autotest/autotest.qrc index 6bd7eac6d79..a62ab49d8fb 100644 --- a/src/plugins/autotest/autotest.qrc +++ b/src/plugins/autotest/autotest.qrc @@ -11,5 +11,9 @@ images/runselected_tickmarks.png images/runselected_tickmarks@2x.png images/data.png + images/text.png + images/text@2x.png + images/visual.png + images/visual@2x.png diff --git a/src/plugins/autotest/autotesticons.h b/src/plugins/autotest/autotesticons.h index cb557b5390e..b278509dbbc 100644 --- a/src/plugins/autotest/autotesticons.h +++ b/src/plugins/autotest/autotesticons.h @@ -71,6 +71,8 @@ const Utils::Icon RESULT_MESSAGEWARN({ const Utils::Icon RESULT_MESSAGEFATAL({ {":/utils/images/filledcircle.png", Utils::Theme::OutputPanes_TestFatalTextColor}}, 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 Autotest diff --git a/src/plugins/autotest/gtest/gtestoutputreader.cpp b/src/plugins/autotest/gtest/gtestoutputreader.cpp index 2eba52e7537..96382f5f896 100644 --- a/src/plugins/autotest/gtest/gtestoutputreader.cpp +++ b/src/plugins/autotest/gtest/gtestoutputreader.cpp @@ -56,20 +56,7 @@ void GTestOutputReader::processOutput(const QByteArray &outputLine) static QRegExp errorLocation("^(.*)\\((\\d+)\\): error:.*$"); static QRegExp iterations("^Repeating all tests \\(iteration (\\d+)\\) \\. \\. \\.$"); - QByteArray read = 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); + const QString line = QString::fromLatin1(outputLine); if (line.trimmed().isEmpty()) return; diff --git a/src/plugins/autotest/gtest/gtestoutputreader.h b/src/plugins/autotest/gtest/gtestoutputreader.h index 42031cd4d67..0ed2bf878cb 100644 --- a/src/plugins/autotest/gtest/gtestoutputreader.h +++ b/src/plugins/autotest/gtest/gtestoutputreader.h @@ -50,7 +50,6 @@ private: QString m_currentTestName; QString m_currentTestSet; QString m_description; - QByteArray m_unprocessed; int m_iteration = 1; }; diff --git a/src/plugins/autotest/images/text.png b/src/plugins/autotest/images/text.png new file mode 100644 index 00000000000..b43b92e09a1 Binary files /dev/null and b/src/plugins/autotest/images/text.png differ diff --git a/src/plugins/autotest/images/text@2x.png b/src/plugins/autotest/images/text@2x.png new file mode 100644 index 00000000000..c93133a2d77 Binary files /dev/null and b/src/plugins/autotest/images/text@2x.png differ diff --git a/src/plugins/autotest/images/visual.png b/src/plugins/autotest/images/visual.png new file mode 100644 index 00000000000..092367618c0 Binary files /dev/null and b/src/plugins/autotest/images/visual.png differ diff --git a/src/plugins/autotest/images/visual@2x.png b/src/plugins/autotest/images/visual@2x.png new file mode 100644 index 00000000000..1426597449f Binary files /dev/null and b/src/plugins/autotest/images/visual@2x.png differ diff --git a/src/plugins/autotest/testoutputreader.cpp b/src/plugins/autotest/testoutputreader.cpp index b7cf59f1d58..fea9237717d 100644 --- a/src/plugins/autotest/testoutputreader.cpp +++ b/src/plugins/autotest/testoutputreader.cpp @@ -25,6 +25,7 @@ #include "testoutputreader.h" #include "testresult.h" +#include "testresultspane.h" #include #include @@ -41,12 +42,21 @@ TestOutputReader::TestOutputReader(const QFutureInterface &future if (m_testApplication) { connect(m_testApplication, &QProcess::readyRead, this, [this] () { - while (m_testApplication->canReadLine()) - processOutput(m_testApplication->readLine()); + while (m_testApplication->canReadLine()) { + 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, this, [this] () { - processStdError(m_testApplication->readAllStandardError()); + const QByteArray output = m_testApplication->readAllStandardError(); + emit newOutputAvailable(output); + processStdError(output); }); } } diff --git a/src/plugins/autotest/testoutputreader.h b/src/plugins/autotest/testoutputreader.h index 237f23c6aca..ff04d8f4ce6 100644 --- a/src/plugins/autotest/testoutputreader.h +++ b/src/plugins/autotest/testoutputreader.h @@ -45,6 +45,8 @@ public: virtual void processOutput(const QByteArray &outputLine) = 0; virtual void processStdError(const QByteArray &output); +signals: + void newOutputAvailable(const QByteArray &output); protected: QFutureInterface m_futureInterface; QProcess *m_testApplication; // not owned diff --git a/src/plugins/autotest/testresultspane.cpp b/src/plugins/autotest/testresultspane.cpp index 48402bbb05a..b4fbab97859 100644 --- a/src/plugins/autotest/testresultspane.cpp +++ b/src/plugins/autotest/testresultspane.cpp @@ -33,16 +33,15 @@ #include "testtreemodel.h" #include "testcodeparser.h" +#include #include #include +#include #include #include #include - #include - #include - #include #include @@ -54,6 +53,7 @@ #include #include #include +#include #include #include @@ -79,11 +79,13 @@ TestResultsPane::TestResultsPane(QObject *parent) : Core::IOutputPane(parent), 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; outputLayout->setMargin(0); outputLayout->setSpacing(0); - m_outputWidget->setLayout(outputLayout); + visualOutputWidget->setLayout(outputLayout); QPalette pal; pal.setColor(QPalette::Window, @@ -103,7 +105,7 @@ TestResultsPane::TestResultsPane(QObject *parent) : outputLayout->addWidget(m_summaryWidget); - m_treeView = new ResultsTreeView(m_outputWidget); + m_treeView = new ResultsTreeView(visualOutputWidget); m_treeView->setHeaderHidden(true); m_treeView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); m_treeView->setContextMenuPolicy(Qt::CustomContextMenu); @@ -119,6 +121,19 @@ TestResultsPane::TestResultsPane(QObject *parent) : 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(); connect(m_treeView, &Utils::TreeView::activated, this, &TestResultsPane::onItemActivated); @@ -188,6 +203,11 @@ void TestResultsPane::createToolButtons() initializeFilterMenu(); connect(m_filterMenu, &QMenu::triggered, this, &TestResultsPane::filterMenuTriggered); 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; @@ -202,6 +222,8 @@ TestResultsPane *TestResultsPane::instance() TestResultsPane::~TestResultsPane() { delete m_treeView; + if (!m_outputWidget->parent()) + delete m_outputWidget; s_instance = nullptr; } @@ -217,6 +239,11 @@ void TestResultsPane::addTestResult(const TestResultPtr &result) navigateStateChanged(); } +void TestResultsPane::addOutput(const QByteArray &output) +{ + m_textOutput->appendPlainText(QString::fromLatin1(output)); +} + QWidget *TestResultsPane::outputWidget(QWidget *parent) { if (m_outputWidget) { @@ -229,7 +256,8 @@ QWidget *TestResultsPane::outputWidget(QWidget *parent) QList 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 @@ -251,6 +279,7 @@ void TestResultsPane::clearContents() m_autoScroll = AutotestPlugin::instance()->settings()->autoScroll; connect(m_treeView->verticalScrollBar(), &QScrollBar::rangeChanged, this, &TestResultsPane::onScrollBarRangeChanged, Qt::UniqueConnection); + m_textOutput->clear(); } 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() QString TestResultsPane::getWholeOutput(const QModelIndex &parent) { diff --git a/src/plugins/autotest/testresultspane.h b/src/plugins/autotest/testresultspane.h index 9c5af8f50cf..6b837055672 100644 --- a/src/plugins/autotest/testresultspane.h +++ b/src/plugins/autotest/testresultspane.h @@ -38,6 +38,8 @@ class QKeyEvent; class QLabel; class QModelIndex; class QMenu; +class QPlainTextEdit; +class QStackedWidget; class QToolButton; QT_END_NAMESPACE @@ -88,6 +90,7 @@ public: void goToPrev() override; void addTestResult(const TestResultPtr &result); + void addOutput(const QByteArray &output); private: explicit TestResultsPane(QObject *parent = 0); @@ -109,9 +112,10 @@ private: void onCopyItemTriggered(const QModelIndex &idx); void onCopyWholeTriggered(); void onSaveWholeTriggered(); + void toggleOutputStyle(); QString getWholeOutput(const QModelIndex &parent = QModelIndex()); - QWidget *m_outputWidget; + QStackedWidget *m_outputWidget; QFrame *m_summaryWidget; QLabel *m_summaryLabel; ResultsTreeView *m_treeView; @@ -123,6 +127,8 @@ private: QToolButton *m_runSelected; QToolButton *m_stopTestRun; QToolButton *m_filterButton; + QToolButton *m_outputToggleButton; + QPlainTextEdit *m_textOutput; QMenu *m_filterMenu; bool m_wasVisibleBefore = false; bool m_autoScroll = false; diff --git a/src/plugins/autotest/testrunner.cpp b/src/plugins/autotest/testrunner.cpp index ea83e1728c1..846a3f81220 100644 --- a/src/plugins/autotest/testrunner.cpp +++ b/src/plugins/autotest/testrunner.cpp @@ -157,6 +157,8 @@ static void performTestRun(QFutureInterface &futureInterface, QScopedPointer outputReader; outputReader.reset(testConfiguration->outputReader(futureInterface, &testProcess)); QTC_ASSERT(outputReader, continue); + TestRunner::connect(outputReader.data(), &TestOutputReader::newOutputAvailable, + TestResultsPane::instance(), &TestResultsPane::addOutput); if (futureInterface.isCanceled()) break; @@ -364,7 +366,8 @@ void TestRunner::debugTests() if (useOutputProcessor) { TestOutputReader *outputreader = config->outputReader(*futureInterface, 0); - + connect(outputreader, &TestOutputReader::newOutputAvailable, + TestResultsPane::instance(), &TestResultsPane::addOutput); connect(runControl, &ProjectExplorer::RunControl::appendMessageRequested, this, [this, outputreader] (ProjectExplorer::RunControl *, const QString &msg, Utils::OutputFormat format) {