diff options
author | Christian Stenger <christian.stenger@theqtcompany.com> | 2015-05-13 16:07:31 +0200 |
---|---|---|
committer | Christian Stenger <christian.stenger@theqtcompany.com> | 2015-06-12 13:15:53 +0300 |
commit | 4605814c1d48f31fd150f32c2687596aeb3cb74e (patch) | |
tree | e5a740eac23f861482df44824dd63a628b736d93 | |
parent | 9d4509540b7a67fcb4f4087708eb207164bdee5f (diff) | |
download | qt-creator-4605814c1d48f31fd150f32c2687596aeb3cb74e.tar.gz |
Parse Squish's XML output and put it on results pane
Change-Id: I5206a30f11b96bd0ab1a3a360b8f5e8fec0fe5f1
Reviewed-by: Riitta-Leena Miettinen <riitta-leena.miettinen@theqtcompany.com>
Reviewed-by: Robert Loehning <robert.loehning@theqtcompany.com>
-rw-r--r-- | plugins/autotest/autotest.pro | 6 | ||||
-rw-r--r-- | plugins/autotest/autotest.qbs | 4 | ||||
-rw-r--r-- | plugins/autotest/squishxmloutputhandler.cpp | 293 | ||||
-rw-r--r-- | plugins/autotest/squishxmloutputhandler.h | 54 | ||||
-rw-r--r-- | plugins/autotest/testresult.cpp | 68 | ||||
-rw-r--r-- | plugins/autotest/testresult.h | 24 | ||||
-rw-r--r-- | plugins/autotest/testresultdelegate.cpp | 7 | ||||
-rw-r--r-- | plugins/autotest/testresultdelegate.h | 4 | ||||
-rw-r--r-- | plugins/autotest/testresultmodel.cpp | 33 | ||||
-rw-r--r-- | plugins/autotest/testresultmodel.h | 6 | ||||
-rw-r--r-- | plugins/autotest/testresultspane.cpp | 18 | ||||
-rw-r--r-- | plugins/autotest/testresultspane.h | 1 | ||||
-rw-r--r-- | plugins/autotest/testsquishtools.cpp | 29 | ||||
-rw-r--r-- | plugins/autotest/testsquishtools.h | 4 |
14 files changed, 534 insertions, 17 deletions
diff --git a/plugins/autotest/autotest.pro b/plugins/autotest/autotest.pro index 85ab704576..df61df9fe6 100644 --- a/plugins/autotest/autotest.pro +++ b/plugins/autotest/autotest.pro @@ -32,7 +32,8 @@ SOURCES += \ testsquishfilehandler.cpp \ opensquishsuitesdialog.cpp \ testsquishutils.cpp \ - testsquishtools.cpp + testsquishtools.cpp \ + squishxmloutputhandler.cpp HEADERS += \ squishsettings.h \ @@ -60,7 +61,8 @@ HEADERS += \ testsquishfilehandler.h \ opensquishsuitesdialog.h \ testsquishutils.h \ - testsquishtools.h + testsquishtools.h \ + squishxmloutputhandler.h RESOURCES += \ autotest.qrc diff --git a/plugins/autotest/autotest.qbs b/plugins/autotest/autotest.qbs index faf80f0484..18f76116ec 100644 --- a/plugins/autotest/autotest.qbs +++ b/plugins/autotest/autotest.qbs @@ -79,7 +79,9 @@ QtcPlugin { "testsquishutils.cpp", "testsquishutils.h", "testsquishtools.cpp", - "testsquishtools.h" + "testsquishtools.h", + "squishxmloutputhandler.cpp", + "squishxmloutputhandler.h" ] Group { diff --git a/plugins/autotest/squishxmloutputhandler.cpp b/plugins/autotest/squishxmloutputhandler.cpp new file mode 100644 index 0000000000..c9e5aad8fd --- /dev/null +++ b/plugins/autotest/squishxmloutputhandler.cpp @@ -0,0 +1,293 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at +** http://www.qt.io/contact-us +** +** This file is part of the Qt Creator Enterprise Auto Test Add-on. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +****************************************************************************/ + +#include "squishxmloutputhandler.h" +#include "testresultspane.h" + +#include <utils/qtcassert.h> + +#include <QDebug> +#include <QDateTime> +#include <QFile> +#include <QXmlStreamWriter> + +namespace Autotest { +namespace Internal { + +SquishXmlOutputHandler::SquishXmlOutputHandler(QObject *parent) : QObject(parent) +{ + connect(this, &SquishXmlOutputHandler::testResultCreated, + TestResultsPane::instance(), &TestResultsPane::addTestResult, Qt::QueuedConnection); +} + +void SquishXmlOutputHandler::clearForNextRun() +{ + m_xmlReader.clear(); +} + +void SquishXmlOutputHandler::mergeResultFiles(const QStringList &reportFiles, + const QString &resultsDirectory, + const QString &suiteName, QString *errorMessage) +{ + QFile resultsXML(QString::fromLatin1("%1/results.xml").arg(resultsDirectory)); + if (resultsXML.exists()) { + if (errorMessage) + *errorMessage = tr("Could not merge results into single results.xml.\n" + "Destination file \"%1\" already exists.").arg(resultsXML.fileName()); + return; + } + + if (!resultsXML.open(QFile::WriteOnly)) { + if (errorMessage) + *errorMessage = tr("Could not merge results into single results.xml.\n" + "Failed to open file \"%1\"").arg(resultsXML.fileName()); + return; + } + + QXmlStreamWriter xmlWriter(&resultsXML); + xmlWriter.writeStartDocument(QLatin1String("1.0")); + bool isFirstReport = true; + bool isFirstTest = true; + QString lastEpilogTime; + foreach (const QString &caseResult, reportFiles) { + QFile currentResultsFile(caseResult); + if (!currentResultsFile.exists()) + continue; + if (!currentResultsFile.open(QFile::ReadOnly)) + continue; + QXmlStreamReader reader(¤tResultsFile); + while (!reader.atEnd()) { + QXmlStreamReader::TokenType type = reader.readNext(); + switch (type) { + case QXmlStreamReader::StartElement: { + const QStringRef tagName = reader.name(); + // SquishReport of the first results.xml will be taken as is and as this is a + // merged results.xml we add another test tag holding the suite's name + if (tagName == QLatin1String("SquishReport")) { + if (isFirstReport) { + xmlWriter.writeStartElement(tagName.toString()); + xmlWriter.writeAttributes(reader.attributes()); + xmlWriter.writeStartElement(QLatin1String("test")); + xmlWriter.writeAttribute(QLatin1String("name"), suiteName); + isFirstReport = false; + } + break; + } + if (isFirstTest && tagName == QLatin1String("test")) { + // the prolog tag of the first results.xml holds the start time of the suite + // we already wrote the test tag for the suite, but haven't added the start + // time as we didn't know about it, so store information of the current test + // tag (case name), read ahead (prolog tag), write prolog (suite's test tag) + // and finally write test tag (case name) - the prolog tag (for test case) + // will be written outside the if + const QXmlStreamAttributes testAttributes = reader.attributes(); + QXmlStreamReader::TokenType token; + while (!reader.atEnd()) { + token = reader.readNext(); + if (token != QXmlStreamReader::Characters) + break; + } + const QStringRef prolog = reader.name(); + QTC_ASSERT(token == QXmlStreamReader::StartElement + && prolog == QLatin1String("prolog"), + if (errorMessage) + *errorMessage = tr("Error while parsing first test result."); + return); + xmlWriter.writeStartElement(prolog.toString()); + xmlWriter.writeAttributes(reader.attributes()); + xmlWriter.writeEndElement(); + xmlWriter.writeStartElement(QLatin1String("test")); + xmlWriter.writeAttributes(testAttributes); + isFirstTest = false; + } else if (tagName == QLatin1String("epilog")) { + lastEpilogTime + = reader.attributes().value(QLatin1String("time")).toString(); + } + xmlWriter.writeCurrentToken(reader); + break; + } + case QXmlStreamReader::EndElement: + if (reader.name() != QLatin1String("SquishReport")) + xmlWriter.writeCurrentToken(reader); + break; + case QXmlStreamReader::Characters: + xmlWriter.writeCurrentToken(reader); + break; + // ignore the rest + default: + break; + } + } + currentResultsFile.close(); + } + if (!lastEpilogTime.isEmpty()) { + xmlWriter.writeStartElement(QLatin1String("epilog")); + xmlWriter.writeAttribute(QLatin1String("time"), lastEpilogTime); + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndDocument(); +} + +Result::Type resultFromString(const QString &type) +{ + if (type == QLatin1String("LOG")) + return Result::SQUISH_LOG; + if (type == QLatin1String("PASS")) + return Result::SQUISH_PASS; + if (type == QLatin1String("FAIL")) + return Result::SQUISH_FAIL; + if (type == QLatin1String("WARNING")) + return Result::SQUISH_WARN; + if (type == QLatin1String("XFAIL")) + return Result::SQUISH_EXPECTED_FAIL; + if (type == QLatin1String("XPASS")) + return Result::UNEXPECTED_PASS; + if (type == QLatin1String("FATAL")) + return Result::SQUISH_FATAL; + if (type == QLatin1String("ERROR")) + return Result::SQUISH_ERROR; + return Result::SQUISH_LOG; +} + +// this method uses the XML reader to parse output of the Squish results.xml and put it into an +// item that can be used to display inside the test results pane +void SquishXmlOutputHandler::outputAvailable(const QByteArray &output) +{ + static QString name; + static QString details; + static QString logDetails; + static QString time; + static QString file; + static Result::Type type; + static int line = 0; + static bool prepend = false; + + m_xmlReader.addData(output); + + while (!m_xmlReader.atEnd()) { + QXmlStreamReader::TokenType tokenType = m_xmlReader.readNext(); + switch (tokenType) { + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + break; + case QXmlStreamReader::StartElement: { + const QString currentName = m_xmlReader.name().toString(); + // tags verification, message, epilog and test will start a new entry, so, reset values + if (currentName == QLatin1String("verification") + || currentName == QLatin1String("message") + || currentName == QLatin1String("epilog") + || currentName == QLatin1String("test")) { + name = currentName; + details.clear(); + logDetails.clear(); + time.clear(); + file.clear(); + line = 0; + type = Result::SQUISH_LOG; + } else if (currentName == QLatin1String("result")) { + // result tag won't add another entry, but gives more information on enclosing tag + name = currentName; + } + + // description tag could provide information that must be prepended to the former entry + if (currentName == QLatin1String("description")) { + prepend = (name == QLatin1String("result") && m_xmlReader.attributes().isEmpty()); + } else { + foreach (const QXmlStreamAttribute &att, m_xmlReader.attributes()) { + const QString attributeName = att.name().toString(); + if (attributeName == QLatin1String("time")) + time = QDateTime::fromString(att.value().toString(), Qt::ISODate) + .toString(QLatin1String("MMM dd, yyyy h:mm:ss AP")); + else if (attributeName == QLatin1String("file")) + file = att.value().toString(); + else if (attributeName == QLatin1String("line")) + line = att.value().toInt(); + else if (attributeName == QLatin1String("type")) + type = resultFromString(att.value().toString()); + else if (attributeName == QLatin1String("name")) + logDetails = att.value().toString(); + } + } + // handle prolog (test) elements already within the start tag + if (currentName == QLatin1String("prolog")) { + TestResult result(QString(), QString(), QString(), Result::SQUISH_START, + logDetails + QLatin1Char('\n') + time); + result.setFileName(file); + result.setLine(line); + emit testResultCreated(result); + } + break; + } + case QXmlStreamReader::EndElement: { + const QString currentName = m_xmlReader.name().toString(); + // description and result tags are handled differently, test tags are handled by + // prolog tag (which is handled in QXmlStreamReader::StartElement already), + // SquishReport tags will be ignored completely + if (currentName == QLatin1String("epilog")) { + TestResult result(QString(), QString(), QString(), Result::SQUISH_END, time); + emit testResultCreated(result); + } else if (currentName != QLatin1String("description") + && currentName != QLatin1String("prolog") + && currentName != QLatin1String("test") + && currentName != QLatin1String("result") + && currentName != QLatin1String("SquishReport")) { + QString description; + if (!logDetails.isEmpty()) + description = logDetails + QLatin1Char('\n'); + + TestResult result(QString(), QString(), QString(), type); + result.setDescription(description + details.trimmed() + QLatin1Char('\n') + time); + result.setFileName(file); + result.setLine(line); + emit testResultCreated(result); + } + break; + } + case QXmlStreamReader::Characters: { + QStringRef text = m_xmlReader.text(); + if (m_xmlReader.isCDATA() || !text.trimmed().isEmpty()) { + if (!m_xmlReader.isCDATA()) + text = text.trimmed(); + if (prepend) { + if (!logDetails.isEmpty() && (text == QLatin1String("Verified") + || text == QLatin1String("Not Verified"))) { + logDetails.prepend(text + QLatin1String(": ")); + } else { + logDetails = text.toString(); + } + } else { + details.append(text).append(QLatin1Char('\n')); + } + } + break; + } + default: + break; + } + } + + if (m_xmlReader.hasError()) { + // kind of expected as we get the output piece by piece + if (m_xmlReader.error() != QXmlStreamReader::PrematureEndOfDocumentError) + qWarning() << m_xmlReader.error() << m_xmlReader.errorString(); + } +} + +} // namespace Internal +} // namespace Autotest diff --git a/plugins/autotest/squishxmloutputhandler.h b/plugins/autotest/squishxmloutputhandler.h new file mode 100644 index 0000000000..abc59cc5dc --- /dev/null +++ b/plugins/autotest/squishxmloutputhandler.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at +** http://www.qt.io/contact-us +** +** This file is part of the Qt Creator Enterprise Auto Test Add-on. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +****************************************************************************/ + +#ifndef SQUISHXMLOUTPUTHANDLER_H +#define SQUISHXMLOUTPUTHANDLER_H + +#include "testresult.h" + +#include <QObject> +#include <QXmlStreamReader> + +namespace Autotest { +namespace Internal { + +class SquishXmlOutputHandler : public QObject +{ + Q_OBJECT +public: + explicit SquishXmlOutputHandler(QObject *parent = 0); + void clearForNextRun(); + + static void mergeResultFiles(const QStringList &reportFiles, const QString &resultsDirectory, + const QString &suiteName, QString *errorMessage = 0); + +signals: + void testResultCreated(const TestResult &testResult); + +public slots: + void outputAvailable(const QByteArray &output); + +private: + QXmlStreamReader m_xmlReader; +}; + +} // namespace Internal +} // namespace Autotest + +#endif // SQUISHXMLOUTPUTHANDLER_H diff --git a/plugins/autotest/testresult.cpp b/plugins/autotest/testresult.cpp index 6a054a4c7d..2fe919e873 100644 --- a/plugins/autotest/testresult.cpp +++ b/plugins/autotest/testresult.cpp @@ -19,6 +19,8 @@ #include "testresult.h" +#include <utils/qtcassert.h> + namespace Autotest { namespace Internal { @@ -93,6 +95,22 @@ Result::Type TestResult::toResultType(int rt) return Result::MESSAGE_INTERNAL; case Result::MESSAGE_CURRENT_TEST: return Result::MESSAGE_CURRENT_TEST; + case Result::SQUISH_LOG: + return Result::SQUISH_LOG; + case Result::SQUISH_PASS: + return Result::SQUISH_PASS; + case Result::SQUISH_FAIL: + return Result::SQUISH_FAIL; + case Result::SQUISH_EXPECTED_FAIL: + return Result::SQUISH_EXPECTED_FAIL; + case Result::SQUISH_UNEXPECTED_PASS: + return Result::SQUISH_UNEXPECTED_PASS; + case Result::SQUISH_WARN: + return Result::SQUISH_WARN; + case Result::SQUISH_FATAL: + return Result::SQUISH_FATAL; + case Result::SQUISH_START: + return Result::SQUISH_START; default: return Result::UNKNOWN; } @@ -126,6 +144,26 @@ QString TestResult::resultToString(const Result::Type type) return QLatin1String("BPASS"); case Result::BLACKLISTED_FAIL: return QLatin1String("BFAIL"); + case Result::SQUISH_LOG: + return QLatin1String("Log"); + case Result::SQUISH_PASS: + return QLatin1String("Pass"); + case Result::SQUISH_FAIL: + return QLatin1String("Fail"); + case Result::SQUISH_ERROR: + return QLatin1String("Error"); + case Result::SQUISH_FATAL: + return QLatin1String("Fatal"); + case Result::SQUISH_EXPECTED_FAIL: + return QLatin1String("Expected Fail"); + case Result::SQUISH_UNEXPECTED_PASS: + return QLatin1String("Unexpected Pass"); + case Result::SQUISH_WARN: + return QLatin1String("Warning"); + case Result::SQUISH_START: + return QLatin1String("Start"); + case Result::SQUISH_END: + return QLatin1String("Test finished"); default: return QLatin1String("UNKNOWN"); } @@ -157,9 +195,37 @@ QColor TestResult::colorForType(const Result::Type type) case Result::MESSAGE_INTERNAL: case Result::MESSAGE_CURRENT_TEST: return QColor("transparent"); + case Result::SQUISH_LOG: + case Result::SQUISH_START: + case Result::SQUISH_END: + return QColor(0, 0, 0); + case Result::SQUISH_PASS: + return QColor(0, 0x99, 0); + case Result::SQUISH_FAIL: + case Result::SQUISH_ERROR: + return QColor(0xa0, 0, 0); + case Result::SQUISH_EXPECTED_FAIL: + return QColor(0, 0xff, 0); + case Result::SQUISH_UNEXPECTED_PASS: + return QColor(0xff, 0, 0); + case Result::SQUISH_WARN: + return QColor(0x86, 0, 0x86); + case Result::SQUISH_FATAL: + return QColor(0x64, 0, 0); default: - return QColor("#000000"); + return QColor(0, 0, 0); + } +} + +QString TestResult::maxString(const Result::Type type) +{ + if ((type >= Result::QTEST_GROUP_BEGIN && type <= Result::QTEST_GROUP_END) + || type == Result::UNKNOWN) { + return QLatin1String("UNKNOWN"); + } else if (type >= Result::SQUISH_GROUP_BEGIN && type <= Result::SQUISH_GROUP_END) { + return QLatin1String("Unexpected Pass"); } + QTC_ASSERT(false, return QString()); } bool operator==(const TestResult &t1, const TestResult &t2) diff --git a/plugins/autotest/testresult.h b/plugins/autotest/testresult.h index c1d6d5eac4..5037808daa 100644 --- a/plugins/autotest/testresult.h +++ b/plugins/autotest/testresult.h @@ -29,6 +29,7 @@ namespace Internal { namespace Result{ enum Type { + // QTest / Quick Test PASS, FAIL, EXPECTED_FAIL, @@ -42,9 +43,27 @@ enum Type { MESSAGE_FATAL, MESSAGE_INTERNAL, MESSAGE_CURRENT_TEST, - UNKNOWN // ??? + // Squish + SQUISH_LOG, + SQUISH_PASS, + SQUISH_FAIL, + SQUISH_EXPECTED_FAIL, + SQUISH_UNEXPECTED_PASS, + SQUISH_WARN, + SQUISH_ERROR, + SQUISH_FATAL, + SQUISH_START, + SQUISH_END, + // ??? + UNKNOWN, + + // group stuff + QTEST_GROUP_BEGIN = PASS, + QTEST_GROUP_END = MESSAGE_CURRENT_TEST, + SQUISH_GROUP_BEGIN = SQUISH_LOG, + SQUISH_GROUP_END = SQUISH_END }; -} +} // namespace Result class TestResult { @@ -70,6 +89,7 @@ public: static Result::Type toResultType(int rt); static QString resultToString(const Result::Type type); static QColor colorForType(const Result::Type type); + static QString maxString(const Result::Type type); private: QString m_class; diff --git a/plugins/autotest/testresultdelegate.cpp b/plugins/autotest/testresultdelegate.cpp index 0eedd68eef..158e98e7d8 100644 --- a/plugins/autotest/testresultdelegate.cpp +++ b/plugins/autotest/testresultdelegate.cpp @@ -73,9 +73,9 @@ void TestResultDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op painter->setPen(foreground); TestResultFilterModel *resultFilterModel = static_cast<TestResultFilterModel *>(view->model()); TestResultModel *resultModel = static_cast<TestResultModel *>(resultFilterModel->sourceModel()); - LayoutPositions positions(opt, resultModel); TestResult testResult = resultModel->testResult(resultFilterModel->mapToSource(index)); Result::Type type = testResult.result(); + LayoutPositions positions(opt, resultModel, type); QIcon icon = index.data(Qt::DecorationRole).value<QIcon>(); if (!icon.isNull()) @@ -119,6 +119,7 @@ void TestResultDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op output.append(QLatin1Char('\n')).append(desc.mid(breakPos)); } break; + // SQUISH_XYZ will be handled with default as well default: output = desc; if (!selected) @@ -194,7 +195,8 @@ QSize TestResultDelegate::sizeHint(const QStyleOptionViewItem &option, const QMo int fontHeight = fm.height(); TestResultFilterModel *resultFilterModel = static_cast<TestResultFilterModel *>(view->model()); TestResultModel *resultModel = static_cast<TestResultModel *>(resultFilterModel->sourceModel()); - LayoutPositions positions(opt, resultModel); + LayoutPositions positions(opt, resultModel, + TestResult::toResultType(index.data(Result::TypeRole).toInt())); QSize s; s.setWidth(opt.rect.width()); @@ -228,6 +230,7 @@ QSize TestResultDelegate::sizeHint(const QStyleOptionViewItem &option, const QMo output.append(QLatin1Char('\n')).append(desc.mid(breakPos)); } break; + // SQUISH_XYZ will be handled with default as well default: output = desc; } diff --git a/plugins/autotest/testresultdelegate.h b/plugins/autotest/testresultdelegate.h index 8bc5d58ad8..3f06017643 100644 --- a/plugins/autotest/testresultdelegate.h +++ b/plugins/autotest/testresultdelegate.h @@ -48,7 +48,7 @@ private: class LayoutPositions { public: - LayoutPositions(QStyleOptionViewItemV4 &options, TestResultModel *model) + LayoutPositions(QStyleOptionViewItemV4 &options, TestResultModel *model, Result::Type type) : m_totalWidth(options.rect.width()), m_maxFileLength(model->maxWidthOfFileName(options.font)), m_maxLineLength(model->maxWidthOfLineNumber(options.font)), @@ -56,7 +56,7 @@ private: m_top(options.rect.top()), m_bottom(options.rect.bottom()) { - m_typeAreaWidth = QFontMetrics(options.font).width(QLatin1String("XXXXXXXX")); + m_typeAreaWidth = QFontMetrics(options.font).width(TestResult::maxString(type)); int flexibleArea = lineAreaLeft() - textAreaLeft() - ITEM_SPACING; if (m_maxFileLength > flexibleArea / 2) m_realFileLength = flexibleArea / 2; diff --git a/plugins/autotest/testresultmodel.cpp b/plugins/autotest/testresultmodel.cpp index ee10470032..03fc6fb79a 100644 --- a/plugins/autotest/testresultmodel.cpp +++ b/plugins/autotest/testresultmodel.cpp @@ -107,6 +107,9 @@ QVariant TestResultModel::data(const QModelIndex &index, int role) const const TestResult &tr = m_testResults.at(index.row()); return testResultIcon(tr.result()); } + if (role == Result::TypeRole) { + return m_testResults.at(index.row()).result(); + } return QVariant(); } @@ -124,7 +127,8 @@ void TestResultModel::addTestResult(const TestResult &testResult) const QModelIndex changed = index(m_testResults.size() - 1, 0, QModelIndex()); emit dataChanged(changed, changed); } else { - if (!isCurrentTestMssg && position) // decrement only if at least one other item + // decrement only if last message was of type MESSAGE_CURRENT_TEST + if (!isCurrentTestMssg && lastMssg.result() == Result::MESSAGE_CURRENT_TEST) --position; beginInsertRows(QModelIndex(), position, position); m_testResults.insert(position, testResult); @@ -222,7 +226,11 @@ void TestResultFilterModel::enableAllResultTypes() << Result::MESSAGE_WARN << Result::MESSAGE_INTERNAL << Result::MESSAGE_FATAL << Result::UNKNOWN << Result::BLACKLISTED_PASS << Result::BLACKLISTED_FAIL << Result::BENCHMARK - << Result::MESSAGE_CURRENT_TEST; + << Result::MESSAGE_CURRENT_TEST + << Result::SQUISH_LOG << Result::SQUISH_PASS << Result::SQUISH_FAIL + << Result::SQUISH_EXPECTED_FAIL << Result::SQUISH_UNEXPECTED_PASS + << Result::SQUISH_WARN << Result::SQUISH_ERROR << Result::SQUISH_FATAL + << Result::SQUISH_START << Result::SQUISH_END; invalidateFilter(); } @@ -233,7 +241,26 @@ void TestResultFilterModel::toggleTestResultType(Result::Type type) } else { m_enabled.insert(type); } - invalidateFilter(); + + switch (type) { + case Result::PASS: + toggleTestResultType(Result::SQUISH_PASS); + break; + case Result::FAIL: + toggleTestResultType(Result::SQUISH_FAIL); + break; + case Result::EXPECTED_FAIL: + toggleTestResultType(Result::SQUISH_EXPECTED_FAIL); + break; + case Result::UNEXPECTED_PASS: + toggleTestResultType(Result::SQUISH_UNEXPECTED_PASS); + break; + case Result::MESSAGE_WARN: + toggleTestResultType(Result::SQUISH_WARN); + break; + default: + invalidateFilter(); + } } void TestResultFilterModel::clearTestResults() diff --git a/plugins/autotest/testresultmodel.h b/plugins/autotest/testresultmodel.h index 97f3a3a53c..c6924291a5 100644 --- a/plugins/autotest/testresultmodel.h +++ b/plugins/autotest/testresultmodel.h @@ -30,6 +30,12 @@ namespace Autotest { namespace Internal { +namespace Result { + enum ItemRole { + TypeRole = Qt::UserRole + }; +} // namespace Result + class TestResultModel : public QAbstractItemModel { Q_OBJECT diff --git a/plugins/autotest/testresultspane.cpp b/plugins/autotest/testresultspane.cpp index 1378b6cfcd..d2425ce80b 100644 --- a/plugins/autotest/testresultspane.cpp +++ b/plugins/autotest/testresultspane.cpp @@ -170,6 +170,23 @@ void TestResultsPane::addLogoutput(const QString &output) m_runnerServerLog->appendPlainText(output); } +void TestResultsPane::updateSquishSummaryLabel() +{ + const int passes = m_model->resultTypeCount(Result::SQUISH_PASS) + + m_model->resultTypeCount(Result::SQUISH_EXPECTED_FAIL); + const int fails = m_model->resultTypeCount(Result::SQUISH_FAIL) + + m_model->resultTypeCount(Result::SQUISH_UNEXPECTED_PASS); + + const QString labelText = tr("<p><b>Test summary:</b> %1 passes, %2 fails, " + "%3 fatals, %4 errors, %5 warnings.</p>") + .arg(passes).arg(fails).arg(m_model->resultTypeCount(Result::SQUISH_FATAL)) + .arg(m_model->resultTypeCount(Result::SQUISH_ERROR)) + .arg(m_model->resultTypeCount(Result::SQUISH_WARN)); + + m_summaryLabel->setText(labelText); + m_summaryWidget->setVisible(true); +} + QWidget *TestResultsPane::outputWidget(QWidget *parent) { if (m_outputPane) { @@ -331,6 +348,7 @@ void TestResultsPane::initializeFilterMenu() textAndType.insert(Result::MESSAGE_DEBUG, tr("Debug Messages")); textAndType.insert(Result::MESSAGE_WARN, tr("Warning Messages")); textAndType.insert(Result::MESSAGE_INTERNAL, tr("Internal Messages")); + textAndType.insert(Result::SQUISH_LOG, tr("Log Messages")); foreach (Result::Type result, textAndType.keys()) { QAction *action = new QAction(m_filterMenu); action->setText(textAndType.value(result)); diff --git a/plugins/autotest/testresultspane.h b/plugins/autotest/testresultspane.h index 765b6ff9ff..a448fe6f04 100644 --- a/plugins/autotest/testresultspane.h +++ b/plugins/autotest/testresultspane.h @@ -76,6 +76,7 @@ signals: public slots: void addTestResult(const TestResult &result); void addLogoutput(const QString &output); + void updateSquishSummaryLabel(); private slots: void onItemActivated(const QModelIndex &index); diff --git a/plugins/autotest/testsquishtools.cpp b/plugins/autotest/testsquishtools.cpp index 49739271ee..62bde4e62b 100644 --- a/plugins/autotest/testsquishtools.cpp +++ b/plugins/autotest/testsquishtools.cpp @@ -21,6 +21,7 @@ #include "squishsettings.h" #include "testsquishtools.h" #include "testresultspane.h" +#include "squishxmloutputhandler.h" #include <QDebug> // TODO remove @@ -56,10 +57,16 @@ TestSquishTools::TestSquishTools(QObject *parent) m_state(Idle), m_currentResultsXML(0), m_resultsFileWatcher(0), - m_testRunning(false) + m_testRunning(false), + m_xmlOutputHandler(0) { + TestResultsPane *resultPane = TestResultsPane::instance(); connect(this, &TestSquishTools::logOutputReceived, - TestResultsPane::instance(), &TestResultsPane::addLogoutput, Qt::QueuedConnection); + resultPane, &TestResultsPane::addLogoutput, Qt::QueuedConnection); + connect(this, &TestSquishTools::squishTestRunStarted, + resultPane, &TestResultsPane::clearContents); + connect(this, &TestSquishTools::squishTestRunFinished, + resultPane, &TestResultsPane::updateSquishSummaryLabel); } TestSquishTools::~TestSquishTools() @@ -80,6 +87,9 @@ TestSquishTools::~TestSquishTools() delete m_serverProcess; m_serverProcess = 0; } + + if (m_xmlOutputHandler) + delete m_xmlOutputHandler; } struct SquishToolsSettings @@ -135,6 +145,12 @@ void TestSquishTools::runTestCases(const QString &suitePath, const QStringList & << QLatin1String("--resultdir") << QDir::toNativeSeparators(m_currentResultsDirectory); + if (m_xmlOutputHandler) + delete m_xmlOutputHandler; + m_xmlOutputHandler = new SquishXmlOutputHandler(this); + connect(this, &TestSquishTools::resultOutputCreated, + m_xmlOutputHandler, &SquishXmlOutputHandler::outputAvailable, Qt::QueuedConnection); + m_testRunning = true; emit squishTestRunStarted(); startSquishServer(RunTestRequested); @@ -212,9 +228,14 @@ void TestSquishTools::setState(TestSquishTools::State state) if (m_testCases.isEmpty()) { m_request = ServerStopRequested; stopSquishServer(); - // TODO merge result files + QString error; + SquishXmlOutputHandler::mergeResultFiles(m_reportFiles, m_currentResultsDirectory, + QDir(m_suitePath).dirName(), &error); + if (!error.isEmpty()) + QMessageBox::critical(Core::ICore::dialogParent(), tr("Error"), error); logrotateTestResults(); } else { + m_xmlOutputHandler->clearForNextRun(); startSquishRunner(); } break; @@ -590,7 +611,7 @@ void TestSquishTools::onRunnerOutput(const QString) if (firstNonWhitespace(output) == '<') { // output that must be used for the TestResultsPane - qDebug() << "RunnerOutput:" << output; + emit resultOutputCreated(output); } else { foreach (const QByteArray &line, output.split('\n')) { const QByteArray trimmed = line.trimmed(); diff --git a/plugins/autotest/testsquishtools.h b/plugins/autotest/testsquishtools.h index 65e23312ac..45e09a4719 100644 --- a/plugins/autotest/testsquishtools.h +++ b/plugins/autotest/testsquishtools.h @@ -33,6 +33,8 @@ QT_END_NAMESPACE namespace Autotest { namespace Internal { +class SquishXmlOutputHandler; + class TestSquishTools : public QObject { Q_OBJECT @@ -64,6 +66,7 @@ signals: void logOutputReceived(const QString &output); void squishTestRunStarted(); void squishTestRunFinished(); + void resultOutputCreated(const QByteArray &output); private: enum Request @@ -113,6 +116,7 @@ private: QWindowList m_lastTopLevelWindows; bool m_testRunning; qint64 m_readResultsCount; + SquishXmlOutputHandler *m_xmlOutputHandler; }; } // namespace Internal |