diff options
Diffstat (limited to 'tests/arthur/baselineserver/src')
-rw-r--r-- | tests/arthur/baselineserver/src/baselineserver.cpp | 576 | ||||
-rw-r--r-- | tests/arthur/baselineserver/src/baselineserver.h | 141 | ||||
-rw-r--r-- | tests/arthur/baselineserver/src/baselineserver.pro | 30 | ||||
-rw-r--r-- | tests/arthur/baselineserver/src/baselineserver.qrc | 5 | ||||
-rw-r--r-- | tests/arthur/baselineserver/src/main.cpp | 70 | ||||
-rw-r--r-- | tests/arthur/baselineserver/src/report.cpp | 311 | ||||
-rw-r--r-- | tests/arthur/baselineserver/src/report.h | 91 | ||||
-rw-r--r-- | tests/arthur/baselineserver/src/templates/view.html | 79 |
8 files changed, 0 insertions, 1303 deletions
diff --git a/tests/arthur/baselineserver/src/baselineserver.cpp b/tests/arthur/baselineserver/src/baselineserver.cpp deleted file mode 100644 index 6ff0a0c72d..0000000000 --- a/tests/arthur/baselineserver/src/baselineserver.cpp +++ /dev/null @@ -1,576 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 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$ -** GNU Lesser General Public License Usage -** 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. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#define QT_USE_FAST_CONCATENATION -#define QT_USE_FAST_OPERATOR_PLUS - -#include "baselineserver.h" -#include <QBuffer> -#include <QFile> -#include <QDir> -#include <QCoreApplication> -#include <QFileInfo> -#include <QHostInfo> -#include <QTextStream> -#include <QProcess> -#include <QDirIterator> - -// extra fields, for use in image metadata storage -const QString PI_ImageChecksum(QLS("ImageChecksum")); -const QString PI_RunId(QLS("RunId")); -const QString PI_CreationDate(QLS("CreationDate")); - -QString BaselineServer::storage; -QString BaselineServer::url; -QString BaselineServer::settingsFile; - -BaselineServer::BaselineServer(QObject *parent) - : QTcpServer(parent), lastRunIdIdx(0) -{ - QFileInfo me(QCoreApplication::applicationFilePath()); - meLastMod = me.lastModified(); - heartbeatTimer = new QTimer(this); - connect(heartbeatTimer, SIGNAL(timeout()), this, SLOT(heartbeat())); - heartbeatTimer->start(HEARTBEAT*1000); -} - -QString BaselineServer::storagePath() -{ - if (storage.isEmpty()) { - storage = QLS(qgetenv("QT_LANCELOT_DIR")); - if (storage.isEmpty()) - storage = QLS("/var/www"); - } - return storage; -} - -QString BaselineServer::baseUrl() -{ - if (url.isEmpty()) { - url = QLS("http://") - + QHostInfo::localHostName().toLatin1() + '.' - + QHostInfo::localDomainName().toLatin1() + '/'; - } - return url; -} - -QString BaselineServer::settingsFilePath() -{ - if (settingsFile.isEmpty()) { - QString exeName = QCoreApplication::applicationFilePath().section(QLC('/'), -1); - settingsFile = storagePath() + QLC('/') + exeName + QLS(".ini"); - } - return settingsFile; -} - -void BaselineServer::incomingConnection(int socketDescriptor) -{ - QString runId = QDateTime::currentDateTime().toString(QLS("MMMdd-hhmmss")); - if (runId == lastRunId) { - runId += QLC('-') + QString::number(++lastRunIdIdx); - } else { - lastRunId = runId; - lastRunIdIdx = 0; - } - qDebug() << "Server: New connection! RunId:" << runId; - BaselineThread *thread = new BaselineThread(runId, socketDescriptor, this); - connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); - thread->start(); -} - -void BaselineServer::heartbeat() -{ - // The idea is to exit to be restarted when modified, as soon as not actually serving - QFileInfo me(QCoreApplication::applicationFilePath()); - if (me.lastModified() == meLastMod) - return; - if (!me.exists() || !me.isExecutable()) - return; - - //# (could close() here to avoid accepting new connections, to avoid livelock) - //# also, could check for a timeout to force exit, to avoid hung threads blocking - bool isServing = false; - foreach(BaselineThread *thread, findChildren<BaselineThread *>()) { - if (thread->isRunning()) { - isServing = true; - break; - } - } - - if (!isServing) - QCoreApplication::exit(); -} - -BaselineThread::BaselineThread(const QString &runId, int socketDescriptor, QObject *parent) - : QThread(parent), runId(runId), socketDescriptor(socketDescriptor) -{ -} - -void BaselineThread::run() -{ - BaselineHandler handler(runId, socketDescriptor); - exec(); -} - - -BaselineHandler::BaselineHandler(const QString &runId, int socketDescriptor) - : QObject(), runId(runId), connectionEstablished(false) -{ - settings = new QSettings(BaselineServer::settingsFilePath(), QSettings::IniFormat, this); - - if (socketDescriptor == -1) - return; - - connect(&proto.socket, SIGNAL(readyRead()), this, SLOT(receiveRequest())); - connect(&proto.socket, SIGNAL(disconnected()), this, SLOT(receiveDisconnect())); - proto.socket.setSocketDescriptor(socketDescriptor); -} - -const char *BaselineHandler::logtime() -{ - return 0; - //return QTime::currentTime().toString(QLS("mm:ss.zzz")); -} - -bool BaselineHandler::establishConnection() -{ - if (!proto.acceptConnection(&plat)) { - qWarning() << runId << logtime() << "Accepting new connection from" << proto.socket.peerAddress().toString() << "failed." << proto.errorMessage(); - proto.sendBlock(BaselineProtocol::Abort, proto.errorMessage().toLatin1()); // In case the client can hear us, tell it what's wrong. - 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; - - settings->beginGroup("ClientFilters"); - if (!settings->childKeys().isEmpty() && !plat.value(PI_PulseGitBranch).isEmpty()) { // i.e. not adhoc client - // Abort if client does not match the filters - foreach (QString filterKey, settings->childKeys()) { - QString filter = settings->value(filterKey).toString(); - QString platVal = plat.value(filterKey); - if (filter.isEmpty() || platVal.isEmpty()) - continue; // tbd: add a syntax for specifying a "value-must-be-present" filter - if (!platVal.contains(filter)) { - qDebug() << runId << logtime() << "Did not pass client filter on" << filterKey << "; disconnecting."; - proto.sendBlock(BaselineProtocol::Abort, QByteArray("Configured to not do testing for this client or repo, ref. ") + BaselineServer::settingsFilePath().toLatin1()); - proto.socket.disconnectFromHost(); - return false; - } - } - } - settings->endGroup(); - - proto.sendBlock(BaselineProtocol::Ack, QByteArray()); - - report.init(this, runId, plat); - return true; -} - -void BaselineHandler::receiveRequest() -{ - if (!connectionEstablished) { - connectionEstablished = establishConnection(); - return; - } - - QByteArray block; - BaselineProtocol::Command cmd; - if (!proto.receiveBlock(&cmd, &block)) { - qWarning() << runId << logtime() << "Command reception failed. "<< proto.errorMessage(); - QThread::currentThread()->exit(1); - return; - } - - switch(cmd) { - case BaselineProtocol::RequestBaselineChecksums: - provideBaselineChecksums(block); - break; - case BaselineProtocol::AcceptNewBaseline: - storeImage(block, true); - break; - case BaselineProtocol::AcceptMismatch: - storeImage(block, false); - break; - default: - qWarning() << runId << logtime() << "Unknown command received. " << proto.errorMessage(); - proto.sendBlock(BaselineProtocol::UnknownError, QByteArray()); - } -} - - -void BaselineHandler::provideBaselineChecksums(const QByteArray &itemListBlock) -{ - ImageItemList itemList; - QDataStream ds(itemListBlock); - ds >> itemList; - qDebug() << runId << logtime() << "Received request for checksums for" << itemList.count() - << "items in test function" << itemList.at(0).testFunction; - - for (ImageItemList::iterator i = itemList.begin(); i != itemList.end(); ++i) { - i->imageChecksums.clear(); - i->status = ImageItem::BaselineNotFound; - QString prefix = pathForItem(*i, true); - PlatformInfo itemData = fetchItemMetadata(prefix); - if (itemData.contains(PI_ImageChecksum)) { - bool ok = false; - quint64 checksum = itemData.value(PI_ImageChecksum).toULongLong(&ok, 16); - if (ok) { - i->imageChecksums.prepend(checksum); - i->status = ImageItem::Ok; - } - } - } - - // Find and mark blacklisted items - QString context = pathForItem(itemList.at(0), true, false).section(QLC('/'), 0, -2); - if (itemList.count() > 0) { - QFile file(BaselineServer::storagePath() + QLC('/') + context + QLS("/BLACKLIST")); - if (file.open(QIODevice::ReadOnly)) { - QTextStream in(&file); - do { - QString itemName = in.readLine(); - if (!itemName.isNull()) { - for (ImageItemList::iterator i = itemList.begin(); i != itemList.end(); ++i) { - if (i->itemName == itemName) - i->status = ImageItem::IgnoreItem; - } - } - } while (!in.atEnd()); - } - } - - QByteArray block; - QDataStream ods(&block, QIODevice::WriteOnly); - ods << itemList; - proto.sendBlock(BaselineProtocol::Ack, block); - report.addItems(itemList); -} - - -void BaselineHandler::storeImage(const QByteArray &itemBlock, bool isBaseline) -{ - QDataStream ds(itemBlock); - ImageItem item; - ds >> item; - - QString prefix = pathForItem(item, isBaseline); - qDebug() << runId << logtime() << "Received" << (isBaseline ? "baseline" : "mismatched") << "image for:" << item.itemName << "Storing in" << prefix; - - QString msg; - if (isBaseline) - msg = QLS("New baseline image stored: ") + pathForItem(item, true, true) + QLS(FileFormat); - else - msg = BaselineServer::baseUrl() + report.filePath(); - proto.sendBlock(BaselineProtocol::Ack, msg.toLatin1()); - - QString dir = prefix.section(QLC('/'), 0, -2); - QDir cwd; - if (!cwd.exists(dir)) - cwd.mkpath(dir); - item.image.save(prefix + QLS(FileFormat), FileFormat); - - PlatformInfo itemData = plat; - itemData.insert(PI_ImageChecksum, QString::number(item.imageChecksums.at(0), 16)); //# Only the first is stored. TBD: get rid of list - itemData.insert(PI_RunId, runId); - itemData.insert(PI_CreationDate, QDateTime::currentDateTime().toString()); - storeItemMetadata(itemData, prefix); - - if (!isBaseline) - report.addMismatch(item); -} - - -void BaselineHandler::storeItemMetadata(const PlatformInfo &metadata, const QString &path) -{ - QFile file(path + QLS(MetadataFileExt)); - if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { - qWarning() << runId << logtime() << "ERROR: could not write to file" << file.fileName(); - return; - } - QTextStream out(&file); - PlatformInfo::const_iterator it = metadata.constBegin(); - while (it != metadata.constEnd()) { - out << it.key() << ": " << it.value() << endl; - ++it; - } - file.close(); -} - - -PlatformInfo BaselineHandler::fetchItemMetadata(const QString &path) -{ - PlatformInfo res; - QFile file(path + QLS(MetadataFileExt)); - if (!file.open(QIODevice::ReadOnly)) - return res; - QTextStream in(&file); - do { - QString line = in.readLine(); - int idx = line.indexOf(QLS(": ")); - if (idx > 0) - res.insert(line.left(idx), line.mid(idx+2)); - } while (!in.atEnd()); - return res; -} - - -void BaselineHandler::receiveDisconnect() -{ - qDebug() << runId << logtime() << "Client disconnected."; - report.end(); - QThread::currentThread()->exit(0); -} - - -void BaselineHandler::mapPlatformInfo() const -{ - mapped = plat; - - // Map hostname - QString host = plat.value(PI_HostName).section(QLC('.'), 0, 0); // Filter away domain, if any - if (host.isEmpty() || host == QLS("localhost")) { - host = plat.value(PI_HostAddress); - } else { - if (!plat.value(PI_PulseGitBranch).isEmpty()) { - // i.e. pulse run, so remove index postfix typical of vm hostnames - host.remove(QRegExp(QLS("\\d+$"))); - if (host.endsWith(QLC('-'))) - host.chop(1); - } - } - if (host.isEmpty()) - host = QLS("unknownhost"); - mapped.insert(PI_HostName, host); - - // Map qmakespec - QString mkspec = plat.value(PI_QMakeSpec); - mapped.insert(PI_QMakeSpec, mkspec.replace(QLC('/'), QLC('_'))); - - // Map Qt version - QString ver = plat.value(PI_QtVersion); - mapped.insert(PI_QtVersion, ver.prepend(QLS("Qt-"))); -} - -QString BaselineHandler::pathForItem(const ImageItem &item, bool isBaseline, bool absolute) const -{ - if (mapped.isEmpty()) - mapPlatformInfo(); - - 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'))); - - QStringList path; - if (absolute) - path += BaselineServer::storagePath(); - path += mapped.value(PI_TestCase); - path += QLS(isBaseline ? "baselines" : "mismatches"); - path += item.testFunction; - path += mapped.value(PI_QtVersion); - path += mapped.value(PI_QMakeSpec); - path += mapped.value(PI_HostName); - if (!isBaseline) - path += runId; - path += itemName + QLC('.'); - - return path.join(QLS("/")); -} - - -QString BaselineHandler::view(const QString &baseline, const QString &rendered, const QString &compared) -{ - QFile f(":/templates/view.html"); - f.open(QIODevice::ReadOnly); - return QString::fromLatin1(f.readAll()).arg('/'+baseline, '/'+rendered, '/'+compared); -} - - -QString BaselineHandler::clearAllBaselines(const QString &context) -{ - int tot = 0; - int failed = 0; - QDirIterator it(BaselineServer::storagePath() + QLC('/') + context, - QStringList() << QLS("*.") + QLS(FileFormat) << QLS("*.") + QLS(MetadataFileExt)); - while (it.hasNext()) { - tot++; - if (!QFile::remove(it.next())) - failed++; - } - return QString(QLS("%1 of %2 baselines cleared from context ")).arg((tot-failed)/2).arg(tot/2) + context; -} - -QString BaselineHandler::updateBaselines(const QString &context, const QString &mismatchContext, const QString &itemFile) -{ - int tot = 0; - int failed = 0; - QString storagePrefix = BaselineServer::storagePath() + QLC('/'); - // If itemId is set, update just that one, otherwise, update all: - QString filter = itemFile.isEmpty() ? QLS("*_????.") : itemFile; - QDirIterator it(storagePrefix + mismatchContext, QStringList() << filter + QLS(FileFormat) << filter + QLS(MetadataFileExt)); - while (it.hasNext()) { - tot++; - it.next(); - QString oldFile = storagePrefix + context + QLC('/') + it.fileName(); - QFile::remove(oldFile); // Remove existing baseline file - if (!QFile::copy(it.filePath(), oldFile)) // and replace it with the mismatch - failed++; - } - return QString(QLS("%1 of %2 baselines updated in context %3 from context %4")).arg((tot-failed)/2).arg(tot/2).arg(context, mismatchContext); -} - -QString BaselineHandler::blacklistTest(const QString &context, const QString &itemId, bool removeFromBlacklist) -{ - QFile file(BaselineServer::storagePath() + QLC('/') + context + QLS("/BLACKLIST")); - QStringList blackList; - if (file.open(QIODevice::ReadWrite)) { - while (!file.atEnd()) - blackList.append(file.readLine().trimmed()); - - if (removeFromBlacklist) - blackList.removeAll(itemId); - else if (!blackList.contains(itemId)) - blackList.append(itemId); - - file.resize(0); - foreach (QString id, blackList) - file.write(id.toLatin1() + '\n'); - file.close(); - return QLS(removeFromBlacklist ? "Whitelisted " : "Blacklisted ") + itemId + QLS(" in context ") + context; - } else { - return QLS("Unable to update blacklisted tests, failed to open ") + file.fileName(); - } -} - - -void BaselineHandler::testPathMapping() -{ - qDebug() << "Storage prefix:" << BaselineServer::storagePath(); - - QStringList hosts; - hosts << QLS("bq-ubuntu910-x86-01") - << QLS("bq-ubuntu910-x86-15") - << QLS("osl-mac-master-5.test.qt.nokia.com") - << QLS("osl-mac-master-6.test.qt.nokia.com") - << QLS("sv-xp-vs-010") - << QLS("sv-xp-vs-011") - << QLS("sv-solaris-sparc-008") - << QLS("macbuilder-02.test.troll.no") - << QLS("bqvm1164") - << QLS("chimera") - << QLS("localhost") - << QLS(""); - - ImageItem item; - item.testFunction = QLS("testPathMapping"); - item.itemName = QLS("arcs.qps"); - item.imageChecksums << 0x0123456789abcdefULL; - item.itemChecksum = 0x0123; - - plat.insert(PI_QtVersion, QLS("5.0.0")); - plat.insert(PI_BuildKey, QLS("(nobuildkey)")); - plat.insert(PI_QMakeSpec, QLS("linux-g++")); - plat.insert(PI_PulseGitBranch, QLS("somebranch")); - foreach(const QString& host, hosts) { - mapped.clear(); - plat.insert(PI_HostName, host); - qDebug() << "Baseline from" << host << "->" << pathForItem(item, true); - qDebug() << "Mismatch from" << host << "->" << pathForItem(item, false); - } -} - - -QString BaselineHandler::computeMismatchScore(const QImage &baseline, const QImage &rendered) -{ - if (baseline.size() != rendered.size() || baseline.format() != rendered.format()) - return QLS("[No score, incomparable images.]"); - if (baseline.depth() != 32) - return QLS("[Score computation not implemented for format.]"); - - int w = baseline.width(); - int h = baseline.height(); - - uint ncd = 0; // number of differing color pixels - uint nad = 0; // number of differing alpha pixels - uint scd = 0; // sum of color pixel difference - uint sad = 0; // sum of alpha pixel difference - - for (int y=0; y<h; ++y) { - const QRgb *bl = (const QRgb *) baseline.constScanLine(y); - const QRgb *rl = (const QRgb *) rendered.constScanLine(y); - for (int x=0; x<w; ++x) { - QRgb b = bl[x]; - QRgb r = rl[x]; - if (r != b) { - int dr = qAbs(qRed(b) - qRed(r)); - int dg = qAbs(qGreen(b) - qGreen(r)); - int db = qAbs(qBlue(b) - qBlue(r)); - int ds = dr + dg + db; - int da = qAbs(qAlpha(b) - qAlpha(r)); - if (ds) { - ncd++; - scd += ds; - } - if (da) { - nad++; - sad += da; - } - } - } - } - - double pcd = 100.0 * ncd / (w*h); // percent of pixels that differ - double acd = ncd ? double(scd) / (3*ncd) : 0; // avg. difference - QString res = QString(QLS("Diffscore: %1% (Num:%2 Avg:%3)")).arg(pcd, 0, 'g', 2).arg(ncd).arg(acd, 0, 'g', 2); - if (baseline.hasAlphaChannel()) { - double pad = 100.0 * nad / (w*h); // percent of pixels that differ - double aad = nad ? double(sad) / (3*nad) : 0; // avg. difference - res += QString(QLS(" Alpha-diffscore: %1% (Num:%2 Avg:%3)")).arg(pad, 0, 'g', 2).arg(nad).arg(aad, 0, 'g', 2); - } - return res; -} diff --git a/tests/arthur/baselineserver/src/baselineserver.h b/tests/arthur/baselineserver/src/baselineserver.h deleted file mode 100644 index d73bb974f9..0000000000 --- a/tests/arthur/baselineserver/src/baselineserver.h +++ /dev/null @@ -1,141 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 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$ -** GNU Lesser General Public License Usage -** 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. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef BASELINESERVER_H -#define BASELINESERVER_H - -#include <QStringList> -#include <QTcpServer> -#include <QThread> -#include <QTcpSocket> -#include <QScopedPointer> -#include <QTimer> -#include <QDateTime> -#include <QSettings> - -#include "baselineprotocol.h" -#include "report.h" - -// #seconds between update checks -#define HEARTBEAT 10 -#define MetadataFileExt "metadata" - -class BaselineServer : public QTcpServer -{ - Q_OBJECT - -public: - BaselineServer(QObject *parent = 0); - - static QString storagePath(); - static QString baseUrl(); - static QString settingsFilePath(); - -protected: - void incomingConnection(int socketDescriptor); - -private slots: - void heartbeat(); - -private: - QTimer *heartbeatTimer; - QDateTime meLastMod; - QString lastRunId; - int lastRunIdIdx; - static QString storage; - static QString url; - static QString settingsFile; -}; - - - -class BaselineThread : public QThread -{ - Q_OBJECT - -public: - BaselineThread(const QString &runId, int socketDescriptor, QObject *parent); - void run(); - -private: - QString runId; - int socketDescriptor; -}; - - -class BaselineHandler : public QObject -{ - Q_OBJECT - -public: - BaselineHandler(const QString &runId, 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 updateBaselines(const QString &context, const QString &mismatchContext, const QString &itemFile); - static QString blacklistTest(const QString &context, const QString &itemId, bool removeFromBlacklist = false); - -private slots: - void receiveRequest(); - void receiveDisconnect(); - -private: - bool establishConnection(); - void provideBaselineChecksums(const QByteArray &itemListBlock); - void storeImage(const QByteArray &itemBlock, bool isBaseline); - void storeItemMetadata(const PlatformInfo &metadata, const QString &path); - PlatformInfo fetchItemMetadata(const QString &path); - void mapPlatformInfo() const; - const char *logtime(); - QString computeMismatchScore(const QImage& baseline, const QImage& rendered); - - BaselineProtocol proto; - PlatformInfo plat; - mutable PlatformInfo mapped; - QString runId; - bool connectionEstablished; - Report report; - QSettings *settings; -}; - -#endif // BASELINESERVER_H diff --git a/tests/arthur/baselineserver/src/baselineserver.pro b/tests/arthur/baselineserver/src/baselineserver.pro deleted file mode 100644 index 770662b5fd..0000000000 --- a/tests/arthur/baselineserver/src/baselineserver.pro +++ /dev/null @@ -1,30 +0,0 @@ -#------------------------------------------------- -# -# Project created by QtCreator 2010-08-11T11:51:09 -# -#------------------------------------------------- - -QT += core network - -# gui needed for QImage -# QT -= gui - -TARGET = baselineserver -DESTDIR = ../bin -CONFIG += console -CONFIG -= app_bundle - -TEMPLATE = app - -include(../../common/baselineprotocol.pri) - -SOURCES += main.cpp \ - baselineserver.cpp \ - report.cpp - -HEADERS += \ - baselineserver.h \ - report.h - -RESOURCES += \ - baselineserver.qrc diff --git a/tests/arthur/baselineserver/src/baselineserver.qrc b/tests/arthur/baselineserver/src/baselineserver.qrc deleted file mode 100644 index b5cd6afadb..0000000000 --- a/tests/arthur/baselineserver/src/baselineserver.qrc +++ /dev/null @@ -1,5 +0,0 @@ -<RCC> - <qresource prefix="/"> - <file>templates/view.html</file> - </qresource> -</RCC> diff --git a/tests/arthur/baselineserver/src/main.cpp b/tests/arthur/baselineserver/src/main.cpp deleted file mode 100644 index 8e5fa4e669..0000000000 --- a/tests/arthur/baselineserver/src/main.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 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$ -** GNU Lesser General Public License Usage -** 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. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include <QtCore/QCoreApplication> -#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 - Report::handleCGIQuery(queryString); - return 0; - } - - if (a.arguments().contains(QLatin1String("-testmapping"))) { - BaselineHandler h(QLS("SomeRunId")); - h.testPathMapping(); - return 0; - } - - BaselineServer server; - if (!server.listen(QHostAddress::Any, BaselineProtocol::ServerPort)) { - qWarning("Failed to listen!"); - return 1; - } - - qDebug() << "\n*****" << argv[0] << "started, ready to serve on port" << BaselineProtocol::ServerPort - << "with baseline protocol version" << BaselineProtocol::ProtocolVersion << "*****\n"; - return a.exec(); -} diff --git a/tests/arthur/baselineserver/src/report.cpp b/tests/arthur/baselineserver/src/report.cpp deleted file mode 100644 index 7c2d6ac6df..0000000000 --- a/tests/arthur/baselineserver/src/report.cpp +++ /dev/null @@ -1,311 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 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$ -** GNU Lesser General Public License Usage -** 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. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "report.h" -#include "baselineprotocol.h" -#include "baselineserver.h" -#include <QDir> -#include <QProcess> -#include <QUrl> - -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 << "<head><title>" << title << "</title></head>\n" - << "<html><body><h1>" << title << "</h1>\n" - << "<p>Note: This is a <i>static</i> page, generated at " << QDateTime::currentDateTime().toString() - << " for the test run with id " << runId << "</p>\n" - << "<p>Summary: <b><span style=\"color:red\">" << numMismatches << " of " << numItems << "</b></span> items reported mismatching</p>\n\n"; - out << "<h3>Platform Info:</h3>\n" - << "<table>\n"; - foreach (QString key, plat.keys()) - out << "<tr><td>" << key << "</td><td>" << plat.value(key) << "</td></tr>\n"; - out << "</table>\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); - QString misCtx = handler->pathForItem(list.at(0), false, false).section(QLC('/'), 0, -2); - - - out << "\n<p> </p><h3>Test function: " << testFunction << "</h3>\n"; - out << "<p><a href=\"/cgi-bin/server.cgi?cmd=clearAllBaselines&context=" << ctx << "&url=" << pageUrl - << "\"><b>Clear all baselines</b></a> for this testfunction (They will be recreated by the next run)</p>\n"; - out << "<p><a href=\"/cgi-bin/server.cgi?cmd=updateAllBaselines&context=" << ctx << "&mismatchContext=" << misCtx << "&url=" << pageUrl - << "\"><b>Let these mismatching images be the new baselines</b></a> for this testfunction</p>\n\n"; - - out << "<table border=\"2\">\n" - "<tr>\n" - "<th width=123>Item</th>\n" - "<th width=246>Baseline</th>\n" - "<th width=246>Rendered</th>\n" - "<th width=246>Comparison (diffs are <span style=\"color:red\">RED</span>)</th>\n" - "<th width=246>Info/Action</th>\n" - "</tr>\n\n"; - - foreach (const ImageItem &item, list) { - out << "<tr>\n"; - out << "<td>" << item.itemName << "</td>\n"; - QString prefix = handler->pathForItem(item, true, false); - QString baseline = prefix + QLS(FileFormat); - QString metadata = prefix + QLS(MetadataFileExt); - if (item.status == ImageItem::Mismatch) { - QString rendered = handler->pathForItem(item, false, false) + QLS(FileFormat); - QString itemFile = prefix.section(QLC('/'), -1); - writeItem(baseline, rendered, item, itemFile, ctx, misCtx, metadata); - } - else { - out << "<td align=center><a href=\"/" << baseline << "\">image</a> <a href=\"/" << metadata << "\">info</a></td>\n" - << "<td align=center colspan=2><small>n/a</small></td>\n" - << "<td align=center>"; - switch (item.status) { - case ImageItem::BaselineNotFound: - out << "Baseline not found/regenerated"; - break; - case ImageItem::IgnoreItem: - out << "<span style=\"background-color:yellow\">Blacklisted</span> " - << "<a href=\"/cgi-bin/server.cgi?cmd=whitelist&context=" << ctx - << "&itemId=" << item.itemName << "&url=" << pageUrl - << "\">Whitelist this item</a>"; - break; - case ImageItem::Ok: - out << "<span style=\"color:green\"><small>No mismatch reported</small></span>"; - break; - default: - out << "?"; - break; - } - out << "</td>\n"; - } - out << "</tr>\n\n"; - } - - out << "</table>\n"; -} - -void Report::writeItem(const QString &baseline, const QString &rendered, const ImageItem &item, - const QString &itemFile, const QString &ctx, const QString &misCtx, const QString &metadata) -{ - QString compared = generateCompared(baseline, rendered); - QString pageUrl = BaselineServer::baseUrl() + path; - - QStringList images = QStringList() << baseline << rendered << compared; - foreach (const QString& img, images) - out << "<td height=246 align=center><a href=\"/" << img << "\"><img src=\"/" << generateThumbnail(img) << "\"></a></td>\n"; - - out << "<td align=center>\n" - << "<p><span style=\"color:red\">Mismatch reported</span></p>\n" - << "<p><a href=\"/" << metadata << "\">Baseline Info</a>\n" - << "<p><a href=\"/cgi-bin/server.cgi?cmd=updateSingleBaseline&context=" << ctx << "&mismatchContext=" << misCtx - << "&itemFile=" << itemFile << "&url=" << pageUrl << "\">Let this be the new baseline</a></p>\n" - << "<p><a href=\"/cgi-bin/server.cgi?cmd=blacklist&context=" << ctx - << "&itemId=" << item.itemName << "&url=" << pageUrl << "\">Blacklist this item</a></p>\n" - << "<p><a href=\"/cgi-bin/server.cgi?cmd=view&baseline=" << baseline << "&rendered=" << rendered - << "&compared=" << compared << "&url=" << pageUrl << "\">Inspect</a></p>\n" - << "</td>\n"; -} - -void Report::writeFooter() -{ - out << "\n</body></html>\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" - << "<HTML>"; - - 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::updateBaselines(cgiUrl.queryItemValue(QLS("context")), - cgiUrl.queryItemValue(QLS("mismatchContext")), - cgiUrl.queryItemValue(QLS("itemFile"))); - } else if (command == QLS("updateAllBaselines")) { - s << BaselineHandler::updateBaselines(cgiUrl.queryItemValue(QLS("context")), - cgiUrl.queryItemValue(QLS("mismatchContext")), - QString()); - } 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:<br>" << query << "<br>"; - } - s << "<p><a href=\"" << cgiUrl.queryItemValue(QLS("url")) << "\">Back to report</a>"; - s << "</HTML>"; -} diff --git a/tests/arthur/baselineserver/src/report.h b/tests/arthur/baselineserver/src/report.h deleted file mode 100644 index d21102d32f..0000000000 --- a/tests/arthur/baselineserver/src/report.h +++ /dev/null @@ -1,91 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 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$ -** GNU Lesser General Public License Usage -** 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. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef REPORT_H -#define REPORT_H - -#include "baselineprotocol.h" -#include <QFile> -#include <QTextStream> -#include <QMap> -#include <QStringList> - -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 &itemFile, const QString &ctx, const QString &misCtx, const QString &metadata); - 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<QString, ImageItemList> itemLists; - bool written; - int numItems; - int numMismatches; - QTextStream out; -}; - -#endif // REPORT_H diff --git a/tests/arthur/baselineserver/src/templates/view.html b/tests/arthur/baselineserver/src/templates/view.html deleted file mode 100644 index c048f4781c..0000000000 --- a/tests/arthur/baselineserver/src/templates/view.html +++ /dev/null @@ -1,79 +0,0 @@ -<h3>Lancelot Viewer</h3> - -<p> -Zoom: -<input name="zoom" id="z1" type="radio" checked="Checked">1x</input> -<input name="zoom" id="z2" type="radio">2x</input> -<input name="zoom" id="z4" type="radio">4x</input> -</p> - -<p><table> -<tr> -<td><input name="imgselect" id="baseline" type="radio" checked="Checked">Baseline</input></td> -<td>%1</td> -</tr> -<tr> -<td><input name="imgselect" id="rendered" type="radio">Rendered</input></td> -<td>%2</td> -</tr> -<tr> -<td><input name="imgselect" id="compared" type="radio">Differences</input></td> -<td></td> -</tr> -</table></p> - - -<p> -<canvas id="c" width="800" height="800"></canvas> -</p> - -<script> - var canvas = document.getElementById("c"); - var context = canvas.getContext("2d"); - var cat = new Image(); - cat.src = "%1"; - var z = 1; - cat.onload = function() { - context.mozImageSmoothingEnabled = false; - context.drawImage(cat, 0, 0, z*cat.width, z*cat.height); - }; - - var bbut = document.getElementById("baseline"); - bbut.onclick = function() { - cat.src = "%1"; - }; - - var rbut = document.getElementById("rendered"); - rbut.onclick = function() { - cat.src = "%2"; - }; - - var cbut = document.getElementById("compared"); - cbut.onclick = function() { - cat.src = "%3"; - }; - - function setZoom(zoom) - { - z = zoom; - canvas.width = z*800; - canvas.height = z*800; - context.mozImageSmoothingEnabled = false; - context.drawImage(cat, 0, 0, z*cat.width, z*cat.height); - } - - var z1but = document.getElementById("z1"); - z1but.onclick = function() { - setZoom(1); - }; - - var z2but = document.getElementById("z2"); - z2but.onclick = function() { - setZoom(2); - }; - - var z4but = document.getElementById("z4"); - z4but.onclick = function() { - setZoom(4); - }; -</script> |