From 3c20e7a08aadb08955603d004054a3adacff180f Mon Sep 17 00:00:00 2001 From: aavit Date: Tue, 14 Dec 2010 15:24:20 +0100 Subject: Make the lancelot/baseline test system generically usable To facilitate usage outside of tst_lancelot: - Added new QBaseLineTest simple API - Added "tst_baselineexample" autotest to show usage - Improved reporting - Results from all testfunctions collected in same report --- tests/arthur/baselineserver/src/baselineserver.cpp | 84 +++--- tests/arthur/baselineserver/src/baselineserver.h | 12 +- tests/arthur/baselineserver/src/baselineserver.pro | 4 +- tests/arthur/baselineserver/src/htmlpage.cpp | 244 ----------------- tests/arthur/baselineserver/src/htmlpage.h | 79 ------ tests/arthur/baselineserver/src/main.cpp | 2 +- tests/arthur/baselineserver/src/report.cpp | 296 +++++++++++++++++++++ tests/arthur/baselineserver/src/report.h | 90 +++++++ tests/arthur/common/baselineprotocol.cpp | 5 + tests/arthur/common/baselineprotocol.h | 12 +- tests/arthur/common/qbaselinetest.cpp | 124 +++++++++ tests/arthur/common/qbaselinetest.h | 64 +++++ tests/arthur/common/qbaselinetest.pri | 13 + tests/auto/baselineexample/baselineexample.pro | 18 ++ tests/auto/baselineexample/tst_baselineexample.cpp | 158 +++++++++++ tests/auto/lancelot/lancelot.pro | 5 +- tests/auto/lancelot/tst_lancelot.cpp | 2 +- 17 files changed, 834 insertions(+), 378 deletions(-) delete mode 100644 tests/arthur/baselineserver/src/htmlpage.cpp delete mode 100644 tests/arthur/baselineserver/src/htmlpage.h create mode 100644 tests/arthur/baselineserver/src/report.cpp create mode 100644 tests/arthur/baselineserver/src/report.h create mode 100644 tests/arthur/common/qbaselinetest.cpp create mode 100644 tests/arthur/common/qbaselinetest.h create mode 100644 tests/arthur/common/qbaselinetest.pri create mode 100644 tests/auto/baselineexample/baselineexample.pro create mode 100644 tests/auto/baselineexample/tst_baselineexample.cpp diff --git a/tests/arthur/baselineserver/src/baselineserver.cpp b/tests/arthur/baselineserver/src/baselineserver.cpp index 90eb594..6351dff 100644 --- a/tests/arthur/baselineserver/src/baselineserver.cpp +++ b/tests/arthur/baselineserver/src/baselineserver.cpp @@ -145,36 +145,43 @@ const char *BaselineHandler::logtime() //return QTime::currentTime().toString(QLS("mm:ss.zzz")); } -void BaselineHandler::receiveRequest() +bool BaselineHandler::establishConnection() { - if (!connectionEstablished) { - if (!proto.acceptConnection(&plat)) { - qWarning() << runId << logtime() << "Accepting new connection from" << proto.socket.peerAddress().toString() << "failed." << proto.errorMessage(); - proto.socket.disconnectFromHost(); - return; - } - QString logMsg; - foreach (QString key, plat.keys()) { - if (key != PI_HostName && key != PI_HostAddress) - logMsg += key + QLS(": '") + plat.value(key) + QLS("', "); - } - qDebug() << runId << logtime() << "Connection established with" << plat.value(PI_HostName) - << "[" << qPrintable(plat.value(PI_HostAddress)) << "]" << logMsg; + if (!proto.acceptConnection(&plat)) { + qWarning() << runId << logtime() << "Accepting new connection from" << proto.socket.peerAddress().toString() << "failed." << proto.errorMessage(); + proto.socket.disconnectFromHost(); + return false; + } + QString logMsg; + foreach (QString key, plat.keys()) { + if (key != PI_HostName && key != PI_HostAddress) + logMsg += key + QLS(": '") + plat.value(key) + QLS("', "); + } + qDebug() << runId << logtime() << "Connection established with" << plat.value(PI_HostName) + << "[" << qPrintable(plat.value(PI_HostAddress)) << "]" << logMsg; - // Filter on branch - QString branch = plat.value(PI_PulseGitBranch); - if (branch.isEmpty()) { - // Not run by Pulse, i.e. ad hoc run: Ok. - } - else if (branch != QLS("master-integration") || !plat.value(PI_GitCommit).contains(QLS("Merge branch 'master' of scm.dev.nokia.troll.no:qt/oslo-staging-2 into master-integration"))) { - qDebug() << runId << logtime() << "Did not pass branch/staging repo filter, disconnecting."; - proto.sendBlock(BaselineProtocol::Abort, QByteArray("This branch/staging repo is not assigned to be tested.")); - proto.socket.disconnectFromHost(); - return; - } + // Filter on branch + QString branch = plat.value(PI_PulseGitBranch); + if (branch.isEmpty()) { + // Not run by Pulse, i.e. ad hoc run: Ok. + } + else if (branch != QLS("master-integration") || !plat.value(PI_GitCommit).contains(QLS("Merge branch 'master' of scm.dev.nokia.troll.no:qt/oslo-staging-2 into master-integration"))) { + qDebug() << runId << logtime() << "Did not pass branch/staging repo filter, disconnecting."; + proto.sendBlock(BaselineProtocol::Abort, QByteArray("This branch/staging repo is not assigned to be tested.")); + proto.socket.disconnectFromHost(); + return false; + } + + proto.sendBlock(BaselineProtocol::Ack, QByteArray()); + + report.init(this, runId, plat); + return true; +} - proto.sendBlock(BaselineProtocol::Ack, QByteArray()); - connectionEstablished = true; +void BaselineHandler::receiveRequest() +{ + if (!connectionEstablished) { + connectionEstablished = establishConnection(); return; } @@ -247,7 +254,7 @@ void BaselineHandler::provideBaselineChecksums(const QByteArray &itemListBlock) QDataStream ods(&block, QIODevice::WriteOnly); ods << itemList; proto.sendBlock(BaselineProtocol::Ack, block); - report.start(BaselineServer::storagePath(), runId, plat, context, itemList); + report.addItems(itemList); } @@ -274,19 +281,15 @@ void BaselineHandler::storeImage(const QByteArray &itemBlock, bool isBaseline) file.close(); if (!isBaseline) - report.addItem(pathForItem(item, true, false) + QLS(FileFormat), - pathForItem(item, false, false) + QLS(FileFormat), - item); + report.addMismatch(item); - QByteArray msg(isBaseline ? "New baseline image stored: " : - "Mismatch report: " ); - msg += BaselineServer::baseUrl(); + QString msg; if (isBaseline) - msg += pathForItem(item, true, false).toLatin1() + FileFormat; + msg = QLS("New baseline image stored: ") + pathForItem(item, true, true) + QLS(FileFormat); else - msg += report.filePath(); + msg = BaselineServer::baseUrl() + report.filePath(); - proto.sendBlock(BaselineProtocol::Ack, msg); + proto.sendBlock(BaselineProtocol::Ack, msg.toLatin1()); } @@ -298,7 +301,7 @@ void BaselineHandler::receiveDisconnect() } -void BaselineHandler::mapPlatformInfo() +void BaselineHandler::mapPlatformInfo() const { mapped = plat; @@ -328,12 +331,13 @@ void BaselineHandler::mapPlatformInfo() mapped.insert(PI_QtVersion, ver.prepend(QLS("Qt-"))); //### TBD: remove patch version } -QString BaselineHandler::pathForItem(const ImageItem &item, bool isBaseline, bool absolute) +QString BaselineHandler::pathForItem(const ImageItem &item, bool isBaseline, bool absolute) const { if (mapped.isEmpty()) mapPlatformInfo(); - QString itemName = item.itemName; + QString itemName = item.itemName.simplified(); + itemName.replace(QLC(' '), QLC('_')); itemName.replace(QLC('.'), QLC('_')); itemName.append(QLC('_')); itemName.append(QString::number(item.itemChecksum, 16).rightJustified(4, QLC('0'))); diff --git a/tests/arthur/baselineserver/src/baselineserver.h b/tests/arthur/baselineserver/src/baselineserver.h index 346ce1f..e09a9d0 100644 --- a/tests/arthur/baselineserver/src/baselineserver.h +++ b/tests/arthur/baselineserver/src/baselineserver.h @@ -50,7 +50,7 @@ #include #include "baselineprotocol.h" -#include "htmlpage.h" +#include "report.h" // #seconds between update checks #define HEARTBEAT 10 @@ -99,7 +99,9 @@ class BaselineHandler : public QObject public: BaselineHandler(int socketDescriptor = -1); void testPathMapping(); + QString pathForItem(const ImageItem &item, bool isBaseline = true, bool absolute = true) const; + // CGI callbacks: static QString view(const QString &baseline, const QString &rendered, const QString &compared); static QString clearAllBaselines(const QString &context); static QString updateSingleBaseline(const QString &oldBaseline, const QString &newBaseline); @@ -110,19 +112,19 @@ private slots: void receiveDisconnect(); private: + bool establishConnection(); void provideBaselineChecksums(const QByteArray &itemListBlock); void storeImage(const QByteArray &itemBlock, bool isBaseline); - void mapPlatformInfo(); - QString pathForItem(const ImageItem &item, bool isBaseline = true, bool absolute = true); + void mapPlatformInfo() const; const char *logtime(); QString computeMismatchScore(const QImage& baseline, const QImage& rendered); BaselineProtocol proto; PlatformInfo plat; - PlatformInfo mapped; + mutable PlatformInfo mapped; bool connectionEstablished; QString runId; - HTMLPage report; + Report report; }; #endif // BASELINESERVER_H diff --git a/tests/arthur/baselineserver/src/baselineserver.pro b/tests/arthur/baselineserver/src/baselineserver.pro index defa05a..770662b 100644 --- a/tests/arthur/baselineserver/src/baselineserver.pro +++ b/tests/arthur/baselineserver/src/baselineserver.pro @@ -20,11 +20,11 @@ include(../../common/baselineprotocol.pri) SOURCES += main.cpp \ baselineserver.cpp \ - htmlpage.cpp + report.cpp HEADERS += \ baselineserver.h \ - htmlpage.h + report.h RESOURCES += \ baselineserver.qrc diff --git a/tests/arthur/baselineserver/src/htmlpage.cpp b/tests/arthur/baselineserver/src/htmlpage.cpp deleted file mode 100644 index 2b16d25..0000000 --- a/tests/arthur/baselineserver/src/htmlpage.cpp +++ /dev/null @@ -1,244 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "htmlpage.h" -#include "baselineprotocol.h" -#include "baselineserver.h" -#include -#include -#include - -HTMLPage::HTMLPage() - : headerWritten(false) -{ -} - -HTMLPage::~HTMLPage() -{ - end(); -} - -QString HTMLPage::filePath() -{ - return path; -} - -void HTMLPage::start(const QString &storagepath, const QString &runId, const PlatformInfo pinfo, const QString &context, const ImageItemList &itemList) -{ - end(); - - id = runId; - plat = pinfo; - ctx = context; - root = storagepath + QLC('/'); - imageItems = itemList; - reportDir = pinfo.value(PI_TestCase) + QLC('/') + (pinfo.value(PI_PulseGitBranch).isEmpty() ? QLS("reports/adhoc/") : QLS("reports/pulse/")); - QString dir = root + reportDir; - QDir cwd; - if (!cwd.exists(dir)) - cwd.mkpath(dir); -} - - -void HTMLPage::writeHeader(const ImageItem &item) -{ - path = reportDir + id + QLC('_') + item.testFunction + QLS(".html"); - - QString pageUrl = BaselineServer::baseUrl() + path; - - file.setFileName(root + path); - if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) - qWarning() << "Failed to open report file" << file.fileName(); - out.setDevice(&file); - - out << "

Lancelot results from run " << id << "

\n\n"; - out << "

Platform Info:

\n"; - out << "\n"; - foreach (QString key, plat.keys()) - out << "\n"; - out << "
" << key << "" << plat.value(key) << "

\n\n"; - - out << "

Clear all baselines (They will be recreated by the next run)

\n\n"; - - out << "

\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "
"; -} - - -void HTMLPage::writeFooter() -{ - out << "
ScriptBaselineRenderedComparison (diffs are RED)Info/Action

\n\n"; -} - - -void HTMLPage::addItem(const QString &baseline, const QString &rendered, const ImageItem &item) -{ - if (!headerWritten) { - writeHeader(item); - headerWritten = true; - } - QString compared = generateCompared(baseline, rendered); - QString pageUrl = BaselineServer::baseUrl() + path; - - out << "\n"; - out << "" << item.itemName << "\n"; - QStringList images = QStringList() << baseline << rendered << compared; - foreach(const QString& img, images) - out << "\n"; - - out << "\n" - << "

Replace baseline with rendered

\n" - << "

Blacklist this item

\n" - << "

View

\n" - << "\n"; - out << "\n\n"; - - QMutableVectorIterator it(imageItems); - while (it.hasNext()) { - it.next(); - if (it.value().itemName == item.itemName) { - it.remove(); - break; - } - } -} - - -void HTMLPage::end() -{ - if (file.isOpen()) { - // Add the names of the scripts that passed the test, or were blacklisted - QString pageUrl = BaselineServer::baseUrl() + path; - for (int i=0; i" << imageItems.at(i).itemName << "N/AN/AN/A"; - if (imageItems.at(i).status == ImageItem::IgnoreItem) { - out << "Blacklisted " - << "Whitelist item"; - } else { - out << "Test passed"; - } - out << "\n"; - } - - writeFooter(); - out.flush(); - file.close(); - path.clear(); - headerWritten = false; - } -} - - -QString HTMLPage::generateCompared(const QString &baseline, const QString &rendered, bool fuzzy) -{ - QString res = rendered; - QFileInfo fi(res); - res.chop(fi.suffix().length() + 1); - res += QLS(fuzzy ? "_fuzzycompared.png" : "_compared.png"); - QStringList args; - if (fuzzy) - args << QLS("-fuzz") << QLS("5%"); - args << root+baseline << root+rendered << root+res; - QProcess::execute(QLS("compare"), args); - return res; -} - - -QString HTMLPage::generateThumbnail(const QString &image) -{ - QString res = image; - QFileInfo imgFI(root+image); - res.chop(imgFI.suffix().length() + 1); - res += QLS("_thumbnail.jpg"); - QFileInfo resFI(root+res); - if (resFI.exists() && resFI.lastModified() > imgFI.lastModified()) - return res; - QStringList args; - args << root+image << QLS("-resize") << QLS("240x240") << QLS("-quality") << QLS("50") << root+res; - QProcess::execute(QLS("convert"), args); - return res; -} - - -void HTMLPage::handleCGIQuery(const QString &query) -{ - QUrl cgiUrl(QLS("http://dummy/cgi-bin/dummy.cgi?") + query); - QTextStream s(stdout); - s << "Content-Type: text/html\r\n\r\n" - << ""; - - QString command(cgiUrl.queryItemValue("cmd")); - - if (command == QLS("view")) { - s << BaselineHandler::view(cgiUrl.queryItemValue(QLS("baseline")), - cgiUrl.queryItemValue(QLS("rendered")), - cgiUrl.queryItemValue(QLS("compared"))); - } - else if (command == QLS("updateSingleBaseline")) { - s << BaselineHandler::updateSingleBaseline(cgiUrl.queryItemValue(QLS("oldBaseline")), - cgiUrl.queryItemValue(QLS("newBaseline"))); - } else if (command == QLS("clearAllBaselines")) { - s << BaselineHandler::clearAllBaselines(cgiUrl.queryItemValue(QLS("context"))); - } else if (command == QLS("blacklist")) { - // blacklist a test - s << BaselineHandler::blacklistTest(cgiUrl.queryItemValue(QLS("context")), - cgiUrl.queryItemValue(QLS("itemId"))); - } else if (command == QLS("whitelist")) { - // whitelist a test - s << BaselineHandler::blacklistTest(cgiUrl.queryItemValue(QLS("context")), - cgiUrl.queryItemValue(QLS("itemId")), true); - } else { - s << "Unknown query:
" << query << "
"; - } - s << "

Back to report"; - s << ""; -} diff --git a/tests/arthur/baselineserver/src/htmlpage.h b/tests/arthur/baselineserver/src/htmlpage.h deleted file mode 100644 index 5f1e051..0000000 --- a/tests/arthur/baselineserver/src/htmlpage.h +++ /dev/null @@ -1,79 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef HTMLPAGE_H -#define HTMLPAGE_H - -#include "baselineprotocol.h" -#include -#include - -class HTMLPage -{ -public: - HTMLPage(); - ~HTMLPage(); - - void start(const QString &storagePath, const QString &runId, const PlatformInfo pinfo, const QString &context, const ImageItemList &itemList); - void addItem(const QString &baseline, const QString &rendered, const ImageItem &item); - void end(); - QString filePath(); - - static void handleCGIQuery(const QString &query); - -private: - void writeHeader(const ImageItem &item); - void writeFooter(); - QString generateCompared(const QString &baseline, const QString &rendered, bool fuzzy = false); - QString generateThumbnail(const QString &image); - - QString root; - QString path; - QString reportDir; - QFile file; - QTextStream out; - QString id; - PlatformInfo plat; - QString ctx; - ImageItemList imageItems; - bool headerWritten; -}; - -#endif // HTMLPAGE_H diff --git a/tests/arthur/baselineserver/src/main.cpp b/tests/arthur/baselineserver/src/main.cpp index a5ec4db..bf41f41 100644 --- a/tests/arthur/baselineserver/src/main.cpp +++ b/tests/arthur/baselineserver/src/main.cpp @@ -48,7 +48,7 @@ int main(int argc, char *argv[]) QString queryString(qgetenv("QUERY_STRING")); if (!queryString.isEmpty()) { // run as CGI script - HTMLPage::handleCGIQuery(queryString); + Report::handleCGIQuery(queryString); return 0; } diff --git a/tests/arthur/baselineserver/src/report.cpp b/tests/arthur/baselineserver/src/report.cpp new file mode 100644 index 0000000..f5bb418 --- /dev/null +++ b/tests/arthur/baselineserver/src/report.cpp @@ -0,0 +1,296 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "report.h" +#include "baselineprotocol.h" +#include "baselineserver.h" +#include +#include +#include + +Report::Report() + : written(false), numItems(0), numMismatches(0) +{ +} + +Report::~Report() +{ + end(); +} + +QString Report::filePath() +{ + return path; +} + +void Report::init(const BaselineHandler *h, const QString &r, const PlatformInfo &p) +{ + handler = h; + runId = r; + plat = p; + rootDir = BaselineServer::storagePath() + QLC('/'); + reportDir = plat.value(PI_TestCase) + QLC('/') + (plat.value(PI_PulseGitBranch).isEmpty() ? QLS("reports/adhoc/") : QLS("reports/pulse/")); + QString dir = rootDir + reportDir; + QDir cwd; + if (!cwd.exists(dir)) + cwd.mkpath(dir); + path = reportDir + QLS("Report_") + runId + QLS(".html"); +} + +void Report::addItems(const ImageItemList &items) +{ + if (items.isEmpty()) + return; + numItems += items.size(); + QString func = items.at(0).testFunction; + if (!testFunctions.contains(func)) + testFunctions.append(func); + itemLists[func] += items; +} + +void Report::addMismatch(const ImageItem &item) +{ + if (!testFunctions.contains(item.testFunction)) { + qWarning() << "Report::addMismatch: unknown testfunction" << item.testFunction; + return; + } + bool found = false; + ImageItemList &list = itemLists[item.testFunction]; + for (ImageItemList::iterator it = list.begin(); it != list.end(); ++it) { + if (it->itemName == item.itemName && it->itemChecksum == item.itemChecksum) { + it->status = ImageItem::Mismatch; + found = true; + break; + } + } + if (found) + numMismatches++; + else + qWarning() << "Report::addMismatch: unknown item" << item.itemName << "in testfunction" << item.testFunction; +} + +void Report::end() +{ + if (written || !numMismatches) + return; + write(); + written = true; +} + + +void Report::write() +{ + QFile file(rootDir + path); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + qWarning() << "Failed to open report file" << file.fileName(); + return; + } + out.setDevice(&file); + + writeHeader(); + foreach(const QString &func, testFunctions) { + writeFunctionResults(itemLists.value(func)); + } + writeFooter(); + file.close(); +} + + +void Report::writeHeader() +{ + QString title = plat.value(PI_TestCase) + QLS(" Qt Baseline Test Report"); + out << "" << title << "\n" + << "

" << title << "

\n" + << "

Run Id: " << runId << "

\n" + << "

Summary: " << numMismatches << " of " << numItems << " items reported mismatching

\n\n"; + out << "

Platform Info:

\n" + << "\n"; + foreach (QString key, plat.keys()) + out << "\n"; + out << "
" << key << "" << plat.value(key) << "
\n\n"; +} + + +void Report::writeFunctionResults(const ImageItemList &list) +{ + QString testFunction = list.at(0).testFunction; + QString pageUrl = BaselineServer::baseUrl() + path; + QString ctx = handler->pathForItem(list.at(0), true, false).section(QLC('/'), 0, -2); + + out << "\n

 

Test function: " << testFunction << "

\n"; + out << "

Clear all baselines (They will be recreated by the next run)

\n\n"; + + out << "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n\n"; + + foreach (const ImageItem &item, list) { + out << "\n"; + out << "\n"; + QString baseline = handler->pathForItem(item, true, false) + QLS(FileFormat); + if (item.status == ImageItem::Mismatch) { + QString rendered = handler->pathForItem(item, false, false) + QLS(FileFormat); + writeItem(baseline, rendered, item, ctx); + } + else { + out << "\n" + << "\n" + << "\n"; + } + out << "\n\n"; + } + + out << "
ItemBaselineRenderedComparison (diffs are RED)Info/Action
" << item.itemName << "viewn/a"; + switch (item.status) { + case ImageItem::BaselineNotFound: + out << "Baseline not found/regenerated"; + break; + case ImageItem::IgnoreItem: + out << "Blacklisted " + << "Whitelist this item"; + break; + case ImageItem::Ok: + out << "No mismatch reported"; + break; + default: + out << "?"; + break; + } + out << "
\n"; +} + +void Report::writeItem(const QString &baseline, const QString &rendered, const ImageItem &item, const QString &ctx) +{ + QString compared = generateCompared(baseline, rendered); + QString pageUrl = BaselineServer::baseUrl() + path; + + QStringList images = QStringList() << baseline << rendered << compared; + foreach (const QString& img, images) + out << "\n"; + + out << "\n" + << "

Mismatch reported

\n" + << "

Replace baseline with rendered

\n" + << "

Blacklist this item

\n" + << "

Inspect

\n" + << "\n"; +} + +void Report::writeFooter() +{ + out << "\n\n"; +} + + +QString Report::generateCompared(const QString &baseline, const QString &rendered, bool fuzzy) +{ + QString res = rendered; + QFileInfo fi(res); + res.chop(fi.suffix().length() + 1); + res += QLS(fuzzy ? "_fuzzycompared.png" : "_compared.png"); + QStringList args; + if (fuzzy) + args << QLS("-fuzz") << QLS("5%"); + args << rootDir+baseline << rootDir+rendered << rootDir+res; + QProcess::execute(QLS("compare"), args); + return res; +} + + +QString Report::generateThumbnail(const QString &image) +{ + QString res = image; + QFileInfo imgFI(rootDir+image); + res.chop(imgFI.suffix().length() + 1); + res += QLS("_thumbnail.jpg"); + QFileInfo resFI(rootDir+res); + if (resFI.exists() && resFI.lastModified() > imgFI.lastModified()) + return res; + QStringList args; + args << rootDir+image << QLS("-resize") << QLS("240x240>") << QLS("-quality") << QLS("50") << rootDir+res; + QProcess::execute(QLS("convert"), args); + return res; +} + + +void Report::handleCGIQuery(const QString &query) +{ + QUrl cgiUrl(QLS("http://dummy/cgi-bin/dummy.cgi?") + query); + QTextStream s(stdout); + s << "Content-Type: text/html\r\n\r\n" + << ""; + + QString command(cgiUrl.queryItemValue("cmd")); + + if (command == QLS("view")) { + s << BaselineHandler::view(cgiUrl.queryItemValue(QLS("baseline")), + cgiUrl.queryItemValue(QLS("rendered")), + cgiUrl.queryItemValue(QLS("compared"))); + } + else if (command == QLS("updateSingleBaseline")) { + s << BaselineHandler::updateSingleBaseline(cgiUrl.queryItemValue(QLS("oldBaseline")), + cgiUrl.queryItemValue(QLS("newBaseline"))); + } else if (command == QLS("clearAllBaselines")) { + s << BaselineHandler::clearAllBaselines(cgiUrl.queryItemValue(QLS("context"))); + } else if (command == QLS("blacklist")) { + // blacklist a test + s << BaselineHandler::blacklistTest(cgiUrl.queryItemValue(QLS("context")), + cgiUrl.queryItemValue(QLS("itemId"))); + } else if (command == QLS("whitelist")) { + // whitelist a test + s << BaselineHandler::blacklistTest(cgiUrl.queryItemValue(QLS("context")), + cgiUrl.queryItemValue(QLS("itemId")), true); + } else { + s << "Unknown query:
" << query << "
"; + } + s << "

Back to report"; + s << ""; +} diff --git a/tests/arthur/baselineserver/src/report.h b/tests/arthur/baselineserver/src/report.h new file mode 100644 index 0000000..0df613a --- /dev/null +++ b/tests/arthur/baselineserver/src/report.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef REPORT_H +#define REPORT_H + +#include "baselineprotocol.h" +#include +#include +#include +#include + +class BaselineHandler; + +class Report +{ +public: + Report(); + ~Report(); + + void init(const BaselineHandler *h, const QString &r, const PlatformInfo &p); + void addItems(const ImageItemList& items); + void addMismatch(const ImageItem& item); + void end(); + + QString filePath(); + + static void handleCGIQuery(const QString &query); + +private: + void write(); + void writeFunctionResults(const ImageItemList &list); + void writeItem(const QString &baseline, const QString &rendered, const ImageItem &item, const QString &ctx); + void writeHeader(); + void writeFooter(); + QString generateCompared(const QString &baseline, const QString &rendered, bool fuzzy = false); + QString generateThumbnail(const QString &image); + + const BaselineHandler *handler; + QString runId; + PlatformInfo plat; + QString rootDir; + QString reportDir; + QString path; + QStringList testFunctions; + QMap itemLists; + bool written; + int numItems; + int numMismatches; + QTextStream out; +}; + +#endif // REPORT_H diff --git a/tests/arthur/common/baselineprotocol.cpp b/tests/arthur/common/baselineprotocol.cpp index c7e9e72..e4445bd 100644 --- a/tests/arthur/common/baselineprotocol.cpp +++ b/tests/arthur/common/baselineprotocol.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #ifndef QMAKESPEC #define QMAKESPEC "Unknown" @@ -264,6 +265,10 @@ QDataStream & operator>> (QDataStream &stream, ImageItem &ii) return stream; } +BaselineProtocol::BaselineProtocol() +{ +} + BaselineProtocol::~BaselineProtocol() { socket.close(); diff --git a/tests/arthur/common/baselineprotocol.h b/tests/arthur/common/baselineprotocol.h index 1f4d593..2315bc3 100644 --- a/tests/arthur/common/baselineprotocol.h +++ b/tests/arthur/common/baselineprotocol.h @@ -38,6 +38,7 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ + #ifndef BASELINEPROTOCOL_H #define BASELINEPROTOCOL_H @@ -46,6 +47,7 @@ #include #include #include +#include #define QLS QLatin1String #define QLC QLatin1Char @@ -87,7 +89,8 @@ public: enum ItemStatus { Ok = 0, BaselineNotFound = 1, - IgnoreItem = 2 + IgnoreItem = 2, + Mismatch = 3 }; QString testFunction; @@ -107,12 +110,15 @@ Q_DECLARE_METATYPE(ImageItem); typedef QVector ImageItemList; + class BaselineProtocol { public: - BaselineProtocol() {} + BaselineProtocol(); ~BaselineProtocol(); + static BaselineProtocol *instance(QObject *parent = 0); + // **************************************************** // Important constants here // **************************************************** @@ -136,6 +142,8 @@ public: }; // For client: + + // For advanced client: bool connect(const QString &testCase, bool *dryrun = 0); bool requestBaselineChecksums(const QString &testFunction, ImageItemList *itemList); bool submitNewBaseline(const ImageItem &item, QByteArray *serverMsg); diff --git a/tests/arthur/common/qbaselinetest.cpp b/tests/arthur/common/qbaselinetest.cpp new file mode 100644 index 0000000..e95b510 --- /dev/null +++ b/tests/arthur/common/qbaselinetest.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbaselinetest.h" +#include "baselineprotocol.h" + +namespace QBaselineTest { + +bool connected = false; +bool triedConnecting = false; +BaselineProtocol proto; + + +bool checkImage(const QImage& img, const char *name, quint16 checksum, QByteArray *msg, bool *error) +{ + QByteArray itemName; + bool hasName = qstrlen(name); + const char *tag = QTest::currentDataTag(); + if (qstrlen(tag)) { + itemName = tag; + if (hasName) + itemName.append('_').append(name); + } else { + itemName = hasName ? name : "default_name"; + } + + *msg = "Baseline check of image '" + itemName + "': "; + + if (!triedConnecting) { + triedConnecting = true; + if (!proto.connect(QTest::testObject()->metaObject()->className())) { + *msg += "Failed to connect to baseline server: " + proto.errorMessage().toLatin1(); + *error = true; + return true; + } + connected = true; + } + if (!connected) { + *msg = "Not connected to baseline server."; + *error = true; + return true; + } + + ImageItem item; + item.itemName = QLatin1String(itemName); + item.itemChecksum = checksum; + item.testFunction = QLatin1String(QTest::currentTestFunction()); + ImageItemList list; + list.append(item); + if (!proto.requestBaselineChecksums(QLatin1String(QTest::currentTestFunction()), &list) || list.isEmpty()) { + *msg = "Communication with baseline server failed: " + proto.errorMessage().toLatin1(); + *error = true; + return true; + } + item.image = img; + item.imageChecksums.prepend(ImageItem::computeChecksum(img)); + QByteArray srvMsg; + switch (list.at(0).status) { + case ImageItem::IgnoreItem : + qDebug() << msg->constData() << "Ignored, blacklisted on server."; + return true; + break; + case ImageItem::BaselineNotFound: + if (proto.submitNewBaseline(item, &srvMsg)) + qDebug() << msg->constData() << "Baseline not found on server. New baseline uploaded."; + else + qDebug() << msg->constData() << "Baseline not found on server. Uploading of new baseline failed:" << srvMsg; + return true; + break; + case ImageItem::Ok: + break; + default: + qWarning() << "Unexpected reply from baseline server."; + return true; + break; + } + *error = false; + // The actual comparison of the given image with the baseline: + if (list.at(0).imageChecksums.contains(item.imageChecksums.at(0))) + return true; + proto.submitMismatch(item, &srvMsg); + *msg += "Mismatch. See report:\n " + srvMsg; + return false; +} + +} diff --git a/tests/arthur/common/qbaselinetest.h b/tests/arthur/common/qbaselinetest.h new file mode 100644 index 0000000..3445c72 --- /dev/null +++ b/tests/arthur/common/qbaselinetest.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BASELINETEST_H +#define BASELINETEST_H + +#include + +namespace QBaselineTest { +bool checkImage(const QImage& img, const char *name, quint16 checksum, QByteArray *msg, bool *error); +} + +#define QBASELINE_CHECK_SUM(image, name, checksum)\ +do {\ + QByteArray _msg;\ + bool _err = false;\ + if (!QBaselineTest::checkImage((image), (name), (checksum), &_msg, &_err)) {\ + QFAIL(_msg.constData());\ + } else if (_err) {\ + QSKIP(_msg.constData(), SkipSingle);\ + }\ +} while (0) + +#define QBASELINE_CHECK(image, name) QBASELINE_CHECK_SUM(image, name, 0) + +#endif // BASELINETEST_H diff --git a/tests/arthur/common/qbaselinetest.pri b/tests/arthur/common/qbaselinetest.pri new file mode 100644 index 0000000..5420c6e --- /dev/null +++ b/tests/arthur/common/qbaselinetest.pri @@ -0,0 +1,13 @@ +QT *= testlib + +SOURCES += \ + $$PWD/qbaselinetest.cpp + +HEADERS += \ + $$PWD/qbaselinetest.h + +win32|symbian*:MKSPEC=$$replace(QMAKESPEC, \\\\, /) +else:MKSPEC=$$QMAKESPEC +DEFINES += QMAKESPEC=\\\"$$MKSPEC\\\" + +include($$PWD/baselineprotocol.pri) diff --git a/tests/auto/baselineexample/baselineexample.pro b/tests/auto/baselineexample/baselineexample.pro new file mode 100644 index 0000000..30feecc --- /dev/null +++ b/tests/auto/baselineexample/baselineexample.pro @@ -0,0 +1,18 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2010-12-09T14:55:13 +# +#------------------------------------------------- + +QT += testlib + +TARGET = tst_baselineexample +CONFIG += console +CONFIG -= app_bundle + +TEMPLATE = app + +SOURCES += tst_baselineexample.cpp +DEFINES += SRCDIR=\\\"$$PWD/\\\" + +include($$QT_SOURCE_TREE/tests/arthur/common/qbaselinetest.pri) diff --git a/tests/auto/baselineexample/tst_baselineexample.cpp b/tests/auto/baselineexample/tst_baselineexample.cpp new file mode 100644 index 0000000..28cbec5 --- /dev/null +++ b/tests/auto/baselineexample/tst_baselineexample.cpp @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +class tst_BaselineExample : public QObject +{ + Q_OBJECT + +public: + tst_BaselineExample(); + +private Q_SLOTS: + void testBasicUsage(); + void testMultipleImages(); + void testDataDriven_data(); + void testDataDriven(); + void testDataDrivenMultiple_data(); + void testDataDrivenMultiple(); + void testChecksum_data(); + void testChecksum(); +}; + + +tst_BaselineExample::tst_BaselineExample() +{ +} + + +void tst_BaselineExample::testBasicUsage() +{ + // Generate an image: + QPushButton b("Press me!"); + b.resize(100, 50); + b.show(); + QTest::qWaitForWindowShown(&b); + QImage img1 = QPixmap::grabWidget(&b).toImage(); + QVERIFY(!img1.isNull()); + + // Compare it to baseline on server: + QBASELINE_CHECK(img1, "button"); +} + + +void tst_BaselineExample::testMultipleImages() +{ + QPushButton b("Press me!"); + b.resize(100, 50); + b.show(); + QTest::qWaitForWindowShown(&b); + QBASELINE_CHECK(QPixmap::grabWidget(&b).toImage(), "text1"); + + b.setText("Kick me!"); + QTest::qWait(50); + QBASELINE_CHECK(QPixmap::grabWidget(&b).toImage(), "text2"); +} + + +void tst_BaselineExample::testDataDriven_data() +{ + QTest::addColumn("label"); + QTest::newRow("short") << "Ok!"; + QTest::newRow("long") << "A really long button text that just does not seem to end"; + QTest::newRow("empty") << ""; +} + + +void tst_BaselineExample::testDataDriven() +{ + QFETCH(QString, label); + QPushButton b(label); + b.resize(100, 50); + b.show(); + QTest::qWaitForWindowShown(&b); + QBASELINE_CHECK(QPixmap::grabWidget(&b).toImage(), 0); +} + + +void tst_BaselineExample::testDataDrivenMultiple_data() +{ + testDataDriven_data(); +} + + +void tst_BaselineExample::testDataDrivenMultiple() +{ + QFETCH(QString, label); + QPushButton b(label); + b.resize(100, 50); + b.show(); + QTest::qWaitForWindowShown(&b); + QBASELINE_CHECK(QPixmap::grabWidget(&b).toImage(), "normal"); + + b.setText(label.prepend('&')); + QTest::qWait(50); + QBASELINE_CHECK(QPixmap::grabWidget(&b).toImage(), "shortcut"); +} + + +void tst_BaselineExample::testChecksum_data() +{ + testDataDriven_data(); +} + + +void tst_BaselineExample::testChecksum() +{ + QFETCH(QString, label); + QPushButton b(label); + b.resize(100, 50); + b.show(); + QTest::qWaitForWindowShown(&b); + QBASELINE_CHECK_SUM(QPixmap::grabWidget(&b).toImage(), 0, quint16(qHash(label))); +} + + +QTEST_MAIN(tst_BaselineExample); + +#include "tst_baselineexample.moc" diff --git a/tests/auto/lancelot/lancelot.pro b/tests/auto/lancelot/lancelot.pro index 4535b83..6d6edf8 100644 --- a/tests/auto/lancelot/lancelot.pro +++ b/tests/auto/lancelot/lancelot.pro @@ -7,9 +7,6 @@ SOURCES += tst_lancelot.cpp \ HEADERS += $$QT_SOURCE_TREE/tests/arthur/common/paintcommands.h RESOURCES += $$QT_SOURCE_TREE/tests/arthur/common/images.qrc -include($$QT_SOURCE_TREE/tests/arthur/common/baselineprotocol.pri) -win32|symbian*:MKSPEC=$$replace(QMAKESPEC, \\\\, /) -else:MKSPEC=$$QMAKESPEC -DEFINES += QMAKESPEC=\\\"$$MKSPEC\\\" +include($$QT_SOURCE_TREE/tests/arthur/common/qbaselinetest.pri) !symbian:!wince*:DEFINES += SRCDIR=\\\"$$PWD\\\" diff --git a/tests/auto/lancelot/tst_lancelot.cpp b/tests/auto/lancelot/tst_lancelot.cpp index d1b093f..4bde927 100644 --- a/tests/auto/lancelot/tst_lancelot.cpp +++ b/tests/auto/lancelot/tst_lancelot.cpp @@ -252,7 +252,7 @@ void tst_Lancelot::runTestSuite(GraphicsEngine engine, QImage::Format format) if (dryRunMode) qDebug() << "Dryrun mode, ignoring detected mismatch." << serverMsg; else - QFAIL("Rendered image differs from baseline.\n" + serverMsg); + QFAIL("Rendered image differs from baseline. Report:\n " + serverMsg); } } -- cgit v0.12