From 72b1a614748add429f301ea156760fafc01a5128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Thu, 30 Sep 2010 17:11:09 +0200 Subject: Added support for updating the baselines via the generated HTML reports. The baselineserver can now run as a CGI binary if it detects that the QUERY_STRING variable is set. Currently it only handles commands for updating the baselines for failed tests. For this to work properly the baselineserver binary needs to have the setuid bit set, so that when it's run as a CGI binary, it will have the proper rights to move files around. The server currently assumes the CGI binary is a link to the baselineserver executable named 'server.cgi' in the configured 'cgi-bin' directory. --- tests/arthur/baselineserver/src/baselineserver.cpp | 70 +++++++++++++++++++++- tests/arthur/baselineserver/src/baselineserver.h | 5 ++ tests/arthur/baselineserver/src/htmlpage.cpp | 36 +++++++++++ tests/arthur/baselineserver/src/htmlpage.h | 3 +- tests/arthur/baselineserver/src/main.cpp | 8 ++- 5 files changed, 117 insertions(+), 5 deletions(-) diff --git a/tests/arthur/baselineserver/src/baselineserver.cpp b/tests/arthur/baselineserver/src/baselineserver.cpp index c25bfc4..41e48eb 100644 --- a/tests/arthur/baselineserver/src/baselineserver.cpp +++ b/tests/arthur/baselineserver/src/baselineserver.cpp @@ -6,6 +6,7 @@ #include #include #include +#include QString BaselineServer::storage; @@ -29,6 +30,13 @@ QString BaselineServer::storagePath() return storage; } +QString BaselineServer::baseUrl() +{ + return QLS("http://") + + QHostInfo::localHostName().toLatin1() + '.' + + QHostInfo::localDomainName().toLatin1() + '/'; +} + void BaselineServer::incomingConnection(int socketDescriptor) { qDebug() << "Server: New connection!"; @@ -190,9 +198,7 @@ void BaselineHandler::storeImage(const QByteArray &itemBlock, bool isBaseline) QByteArray msg(isBaseline ? "New baseline image stored: " : "Mismatch report: " ); - msg += "http://" - + QHostInfo::localHostName().toLatin1() + '.' - + QHostInfo::localDomainName().toLatin1() + '/'; + msg += BaselineServer::baseUrl(); if (isBaseline) msg += pathForItem(item, true, false).toLatin1() + FileFormat; else @@ -243,6 +249,64 @@ QString BaselineHandler::pathForItem(const ImageItem &item, bool isBaseline, boo } +QString BaselineHandler::updateAllBaselines(const QString &host, const QString &id, + const QString &engine, const QString &format) +{ + QString basePath(BaselineServer::storagePath()); + QString srcDir(basePath + host + QLC('/') + + QString(QLS("mismatches_%1_%2/")).arg(engine, format) + + id); + QString dstDir(basePath + host + QLC('/') + + QString(QLS("baselines_%1_%2/")).arg(engine, format)); + + QDir dir(srcDir); + QStringList nameFilter; + nameFilter << "*.metadata" << "*.png"; + QStringList fileList = dir.entryList(nameFilter, QDir::Files | QDir::NoDotAndDotDot); + + // remove the generated _fuzzycompared.png and _compared.png files from the list + QMutableStringListIterator it(fileList); + while (it.hasNext()) { + it.next(); + if (it.value().endsWith(QLS("compared.png"))) + it.remove(); + } + + QString res; + QProcess proc; + proc.setWorkingDirectory(srcDir); + proc.setProcessChannelMode(QProcess::MergedChannels); + proc.start(QLS("cp"), QStringList() << QLS("-f") << fileList << dstDir); + proc.waitForFinished(); + if (proc.exitCode() == 0) + res = QLS("Successfully updated baseline for all failed tests."); + else + res = QString("Error updating baseline: %1
" + "Command output:
%2
").arg(proc.errorString(), proc.readAll().constData()); + + return res; +} + +QString BaselineHandler::updateSingleBaseline(const QString &oldBaseline, const QString &newBaseline) +{ + QString res; + QString basePath(BaselineServer::storagePath()); + QString srcBase(basePath + newBaseline.left(newBaseline.length() - 3)); + QString dstDir(basePath + oldBaseline.left(oldBaseline.lastIndexOf(QLC('/')))); + + QProcess proc; + proc.setProcessChannelMode(QProcess::MergedChannels); + proc.start(QLS("cp"), QStringList() << QLS("-f") << srcBase + QLS("png") << srcBase + QLS("metadata") << dstDir); + proc.waitForFinished(); + if (proc.exitCode() == 0) + res = QString("Successfully updated '%1'").arg(oldBaseline + QLS("/metadata")); + else + res = QString("Error updating baseline: %1
" + "Command output:
%2
").arg(proc.errorString(), proc.readAll().constData()); + + return res; +} + void BaselineHandler::testPathMapping() { qDebug() << "Storage prefix:" << BaselineServer::storagePath(); diff --git a/tests/arthur/baselineserver/src/baselineserver.h b/tests/arthur/baselineserver/src/baselineserver.h index 8d791e0..b77bc09 100644 --- a/tests/arthur/baselineserver/src/baselineserver.h +++ b/tests/arthur/baselineserver/src/baselineserver.h @@ -23,6 +23,7 @@ public: BaselineServer(QObject *parent = 0); static QString storagePath(); + static QString baseUrl(); protected: void incomingConnection(int socketDescriptor); @@ -59,6 +60,10 @@ public: BaselineHandler(int socketDescriptor = -1); void testPathMapping(); + static QString updateAllBaselines(const QString &host, const QString &id, + const QString &engine, const QString &format); + static QString updateSingleBaseline(const QString &oldBaseline, const QString &newBaseline); + private slots: void receiveRequest(); void receiveDisconnect(); diff --git a/tests/arthur/baselineserver/src/htmlpage.cpp b/tests/arthur/baselineserver/src/htmlpage.cpp index 3ff138f..4f53c67 100644 --- a/tests/arthur/baselineserver/src/htmlpage.cpp +++ b/tests/arthur/baselineserver/src/htmlpage.cpp @@ -1,7 +1,9 @@ #include "htmlpage.h" #include "baselineprotocol.h" +#include "baselineserver.h" #include #include +#include HTMLPage::HTMLPage() : headerWritten(false) @@ -36,6 +38,9 @@ void HTMLPage::writeHeader(const ImageItem &item) { path = QLS("reports/") + id + QLC('_') + item.engineAsString() + QLC('_') + item.formatAsString() + 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(); @@ -47,6 +52,10 @@ void HTMLPage::writeHeader(const ImageItem &item) out << "

Build key: " << plat.buildKey << "

\n"; out << "

Engine: " << item.engineAsString() << "

\n"; out << "

Format: " << item.formatAsString() << "

\n\n"; + out << "

Update all baselines

\n\n"; out << "\n" "\n" "\n" @@ -55,6 +64,7 @@ void HTMLPage::writeHeader(const ImageItem &item) "\n" "\n" "\n" + "\n" "\n\n"; } @@ -73,12 +83,15 @@ void HTMLPage::addItem(const QString &baseline, const QString &rendered, const I } QString compared = generateCompared(baseline, rendered); QString fuzzy = generateCompared(baseline, rendered, true); + QString pageUrl = BaselineServer::baseUrl() + path; out << "\n"; out << "\n"; QStringList images = QStringList() << baseline << rendered << compared << fuzzy; foreach(const QString& img, images) out << "\n"; + out << "\n"; out << "\n\n"; } @@ -108,3 +121,26 @@ QString HTMLPage::generateCompared(const QString &baseline, const QString &rende QProcess::execute(QLS("compare"), 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" + << ""; +// << "Contents of QUERY_STRING:
" +// << "Full string = " << query << "
"; + + if (cgiUrl.queryItemValue(QLS("update")) == QLS("single")) { + s << BaselineHandler::updateSingleBaseline(cgiUrl.queryItemValue(QLS("oldBaseline")), + cgiUrl.queryItemValue(QLS("newBaseline"))); + } else { + s << BaselineHandler::updateAllBaselines(cgiUrl.queryItemValue(QLS("host")), + cgiUrl.queryItemValue(QLS("id")), + cgiUrl.queryItemValue(QLS("engine")), + cgiUrl.queryItemValue(QLS("format"))); + } + s << "

Back to report"; + s << ""; +} diff --git a/tests/arthur/baselineserver/src/htmlpage.h b/tests/arthur/baselineserver/src/htmlpage.h index af0e364..29b4ff5 100644 --- a/tests/arthur/baselineserver/src/htmlpage.h +++ b/tests/arthur/baselineserver/src/htmlpage.h @@ -14,9 +14,10 @@ public: void start(const QString &storagePath, const QString &runId, const PlatformInfo pinfo); 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(); diff --git a/tests/arthur/baselineserver/src/main.cpp b/tests/arthur/baselineserver/src/main.cpp index a69a5a2..0e15a60 100644 --- a/tests/arthur/baselineserver/src/main.cpp +++ b/tests/arthur/baselineserver/src/main.cpp @@ -1,11 +1,17 @@ #include #include "baselineserver.h" - int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); + QString queryString(qgetenv("QUERY_STRING")); + if (!queryString.isEmpty()) { + // run as CGI script + HTMLPage::handleCGIQuery(queryString); + return 0; + } + if (a.arguments().contains(QLatin1String("-testmapping"))) { BaselineHandler h; h.testPathMapping(); -- cgit v0.12

ScriptComparisonFuzzy ComparisonScoreUpdate
" << item.scriptName << "Update baseline