| " << imageItems.at(i).scriptName << " | N/A | N/A | N/A | ";
+            out << " | | " << imageItems.at(i).itemName << " | N/A | N/A | N/A | ";
             if (imageItems.at(i).status == ImageItem::IgnoreItem) {
                 out << "Blacklisted "
                     << "Whitelist item";
             } else {
                 out << "Test passed";
diff --git a/tests/arthur/common/baselineprotocol.cpp b/tests/arthur/common/baselineprotocol.cpp
index 5ed58b4..c7e9e72 100644
--- a/tests/arthur/common/baselineprotocol.cpp
+++ b/tests/arthur/common/baselineprotocol.cpp
@@ -128,11 +128,10 @@ PlatformInfo::PlatformInfo(bool useLocal)
 
 ImageItem &ImageItem::operator=(const ImageItem &other)
 {
-    scriptName = other.scriptName;
-    scriptChecksum = other.scriptChecksum;
+    testFunction = other.testFunction;
+    itemName = other.itemName;
+    itemChecksum = other.itemChecksum;
     status = other.status;
-    renderFormat = other.renderFormat;
-    engine = other.engine;
     image = other.image;
     imageChecksums = other.imageChecksums;
     return *this;
@@ -165,6 +164,7 @@ quint64 ImageItem::computeChecksum(const QImage &image)
     return (quint64(h1) << 32) | h2;
 }
 
+#if 0
 QString ImageItem::engineAsString() const
 {
     switch (engine) {
@@ -205,6 +205,7 @@ QString ImageItem::formatAsString() const
         return QLS("UnknownFormat");
     return QLS(formatNames[renderFormat]);
 }
+#endif
 
 void ImageItem::writeImageToStream(QDataStream &out) const
 {
@@ -249,20 +250,16 @@ void ImageItem::readImageFromStream(QDataStream &in)
 
 QDataStream & operator<< (QDataStream &stream, const ImageItem &ii)
 {
-    stream << ii.scriptName << ii.scriptChecksum << quint8(ii.status) << quint8(ii.renderFormat)
-           << quint8(ii.engine) << ii.imageChecksums;
+    stream << ii.testFunction << ii.itemName << ii.itemChecksum << quint8(ii.status) << ii.imageChecksums;
     ii.writeImageToStream(stream);
     return stream;
 }
 
 QDataStream & operator>> (QDataStream &stream, ImageItem &ii)
 {
-    quint8 encFormat, encStatus, encEngine;
-    stream >> ii.scriptName >> ii.scriptChecksum >> encStatus >> encFormat
-           >> encEngine >> ii.imageChecksums;
-    ii.renderFormat = QImage::Format(encFormat);
+    quint8 encStatus;
+    stream >> ii.testFunction >> ii.itemName >> ii.itemChecksum >> encStatus >> ii.imageChecksums;
     ii.status = ImageItem::ItemStatus(encStatus);
-    ii.engine = ImageItem::GraphicsEngine(encEngine);
     ii.readImageFromStream(stream);
     return stream;
 }
@@ -275,7 +272,7 @@ BaselineProtocol::~BaselineProtocol()
 }
 
 
-bool BaselineProtocol::connect(bool *dryrun)
+bool BaselineProtocol::connect(const QString &testCase, bool *dryrun)
 {
     errMsg.clear();
     QByteArray serverName(qgetenv("QT_LANCELOT_SERVER"));
@@ -292,6 +289,7 @@ bool BaselineProtocol::connect(bool *dryrun)
     }
 
     PlatformInfo pi(true);
+    pi.insert(PI_TestCase, testCase);
     QByteArray block;
     QDataStream ds(&block, QIODevice::ReadWrite);
     ds << pi;
@@ -342,11 +340,15 @@ bool BaselineProtocol::acceptConnection(PlatformInfo *pi)
 }
 
 
-bool BaselineProtocol::requestBaselineChecksums(ImageItemList *itemList)
+bool BaselineProtocol::requestBaselineChecksums(const QString &testFunction, ImageItemList *itemList)
 {
     errMsg.clear();
     if (!itemList)
         return false;
+
+    for(ImageItemList::iterator it = itemList->begin(); it != itemList->end(); it++)
+        it->testFunction = testFunction;
+
     QByteArray block;
     QDataStream ds(&block, QIODevice::ReadWrite);
     ds << *itemList;
diff --git a/tests/arthur/common/baselineprotocol.h b/tests/arthur/common/baselineprotocol.h
index baffb4a..1f4d593 100644
--- a/tests/arthur/common/baselineprotocol.h
+++ b/tests/arthur/common/baselineprotocol.h
@@ -52,6 +52,7 @@
 
 #define FileFormat "png"
 
+const QString PI_TestCase(QLS("TestCase"));
 const QString PI_HostName(QLS("HostName"));
 const QString PI_HostAddress(QLS("HostAddress"));
 const QString PI_OSName(QLS("OSName"));
@@ -73,19 +74,15 @@ struct ImageItem
 {
 public:
     ImageItem()
-        : status(Ok), renderFormat(QImage::Format_Invalid), engine(Raster), scriptChecksum(0)
+        : status(Ok), itemChecksum(0)
     {}
     ImageItem(const ImageItem &other)
     { *this = other; }
     ~ImageItem()
     {}
     ImageItem &operator=(const ImageItem &other);
-    static quint64 computeChecksum(const QImage& image);
-    QString engineAsString() const;
-    QString formatAsString() const;
 
-    void writeImageToStream(QDataStream &stream) const;
-    void readImageFromStream(QDataStream &stream);
+    static quint64 computeChecksum(const QImage& image);
 
     enum ItemStatus {
         Ok = 0,
@@ -93,19 +90,15 @@ public:
         IgnoreItem = 2
     };
 
-    enum GraphicsEngine {
-        Raster = 0,
-        OpenGL = 1
-    };
-
-    QString scriptName;
+    QString testFunction;
+    QString itemName;
     ItemStatus status;
-    QImage::Format renderFormat;
-    GraphicsEngine engine;
     QImage image;
     QList imageChecksums;
-    // tbd: add diffscore
-    quint16 scriptChecksum;
+    quint16 itemChecksum;
+
+    void writeImageToStream(QDataStream &stream) const;
+    void readImageFromStream(QDataStream &stream);
 };
 QDataStream & operator<< (QDataStream &stream, const ImageItem &ii);
 QDataStream & operator>> (QDataStream &stream, ImageItem& ii);
@@ -124,7 +117,7 @@ public:
     // Important constants here
     // ****************************************************
     enum Constant {
-        ProtocolVersion = 3,
+        ProtocolVersion = 4,
         ServerPort = 54129,
         Timeout = 5000
     };
@@ -143,8 +136,8 @@ public:
     };
 
     // For client:
-    bool connect(bool *dryrun = 0);
-    bool requestBaselineChecksums(ImageItemList *itemList);
+    bool connect(const QString &testCase, bool *dryrun = 0);
+    bool requestBaselineChecksums(const QString &testFunction, ImageItemList *itemList);
     bool submitNewBaseline(const ImageItem &item, QByteArray *serverMsg);
     bool submitMismatch(const ImageItem &item, QByteArray *serverMsg);
 
diff --git a/tests/auto/lancelot/tst_lancelot.cpp b/tests/auto/lancelot/tst_lancelot.cpp
index 7c6fe66..d1b093f 100644
--- a/tests/auto/lancelot/tst_lancelot.cpp
+++ b/tests/auto/lancelot/tst_lancelot.cpp
@@ -66,10 +66,15 @@ public:
     static bool simfail;
 
 private:
-    ImageItem render(const ImageItem &item);
+    enum GraphicsEngine {
+        Raster = 0,
+        OpenGL = 1
+    };
+
+    bool setupTestSuite(const QStringList& blacklist);
+    void runTestSuite(GraphicsEngine engine, QImage::Format format);
+    ImageItem render(const ImageItem &item, GraphicsEngine engine, QImage::Format format);
     void paint(QPaintDevice *device, const QStringList &script, const QString &filePath);
-    void runTestSuite();
-    bool setupTestSuite(ImageItem::GraphicsEngine engine, QImage::Format format, const QStringList& blacklist);
 
     BaselineProtocol proto;
     ImageItemList baseList;
@@ -108,7 +113,7 @@ void tst_Lancelot::initTestCase()
 #if defined(Q_OS_SOMEPLATFORM)
     QSKIP("This test is not supported on this platform.", SkipAll);
 #endif
-    if (!proto.connect(&dryRunMode))
+    if (!proto.connect(QLatin1String("tst_Lancelot"), &dryRunMode))
         QSKIP(qPrintable(proto.errorMessage()), SkipAll);
 
     QDir qpsDir(scriptsDir);
@@ -125,8 +130,8 @@ void tst_Lancelot::initTestCase()
         file.open(QFile::ReadOnly);
         QByteArray cont = file.readAll();
         scripts.insert(fileName, QString::fromLatin1(cont).split(QLatin1Char('\n'), QString::SkipEmptyParts));
-        it->scriptName = fileName;
-        it->scriptChecksum = qChecksum(cont.constData(), cont.size());
+        it->itemName = fileName;
+        it->itemChecksum = qChecksum(cont.constData(), cont.size());
         it++;
     }
 }
@@ -135,42 +140,42 @@ void tst_Lancelot::initTestCase()
 void tst_Lancelot::testRasterARGB32PM_data()
 {
     QStringList localBlacklist;
-    if (!setupTestSuite(ImageItem::Raster, QImage::Format_ARGB32_Premultiplied, localBlacklist))
+    if (!setupTestSuite(localBlacklist))
         QSKIP("Communication with baseline image server failed.", SkipAll);
 }
 
 
 void tst_Lancelot::testRasterARGB32PM()
 {
-    runTestSuite();
+    runTestSuite(Raster, QImage::Format_ARGB32_Premultiplied);
 }
 
 
 void tst_Lancelot::testRasterRGB32_data()
 {
     QStringList localBlacklist;
-    if (!setupTestSuite(ImageItem::Raster, QImage::Format_RGB32, localBlacklist))
+    if (!setupTestSuite(localBlacklist))
         QSKIP("Communication with baseline image server failed.", SkipAll);
 }
 
 
 void tst_Lancelot::testRasterRGB32()
 {
-    runTestSuite();
+    runTestSuite(Raster, QImage::Format_RGB32);
 }
 
 
 void tst_Lancelot::testRasterRGB16_data()
 {
     QStringList localBlacklist;
-    if (!setupTestSuite(ImageItem::Raster, QImage::Format_RGB16, localBlacklist))
+    if (!setupTestSuite(localBlacklist))
         QSKIP("Communication with baseline image server failed.", SkipAll);
 }
 
 
 void tst_Lancelot::testRasterRGB16()
 {
-    runTestSuite();
+    runTestSuite(Raster, QImage::Format_RGB16);
 }
 
 
@@ -178,7 +183,7 @@ void tst_Lancelot::testRasterRGB16()
 void tst_Lancelot::testOpenGL_data()
 {
     QStringList localBlacklist = QStringList() << QLatin1String("rasterops.qps");
-    if (!setupTestSuite(ImageItem::OpenGL, QImage::Format_RGB32, localBlacklist))
+    if (!setupTestSuite(localBlacklist))
         QSKIP("Communication with baseline image server failed.", SkipAll);
 }
 
@@ -197,45 +202,39 @@ void tst_Lancelot::testOpenGL()
             ok = true;
     }
     if (ok)
-        runTestSuite();
+        runTestSuite(OpenGL, QImage::Format_RGB32);
     else
         QSKIP("System under test does not meet preconditions for GL testing. Skipping.", SkipAll);
 }
 #endif
 
 
-bool tst_Lancelot::setupTestSuite(ImageItem::GraphicsEngine engine, QImage::Format format, const QStringList& blacklist)
+bool tst_Lancelot::setupTestSuite(const QStringList& blacklist)
 {
     QTest::addColumn("baseline");
 
     ImageItemList itemList(baseList);
-
-    for(ImageItemList::iterator it = itemList.begin(); it != itemList.end(); it++) {
-        it->engine = engine;
-        it->renderFormat = format;
-    }
-
-    if (!proto.requestBaselineChecksums(&itemList)) {
+    if (!proto.requestBaselineChecksums(QTest::currentTestFunction(), &itemList)) {
         QWARN(qPrintable(proto.errorMessage()));
         return false;
     }
 
     foreach(const ImageItem& item, itemList) {
-        if (!blacklist.contains(item.scriptName))
-            QTest::newRow(item.scriptName.toLatin1()) << item;
+        if (!blacklist.contains(item.itemName))
+            QTest::newRow(item.itemName.toLatin1()) << item;
     }
     return true;
 }
 
 
-void tst_Lancelot::runTestSuite()
+void tst_Lancelot::runTestSuite(GraphicsEngine engine, QImage::Format format)
 {
     QFETCH(ImageItem, baseline);
 
     if (baseline.status == ImageItem::IgnoreItem)
         QSKIP("Blacklisted by baseline server.", SkipSingle);
 
-    ImageItem rendered = render(baseline);
+    ImageItem rendered = render(baseline, engine, format);
     if (rendered.image.isNull()) {    // Assume an error in the test environment, not Qt
         QWARN("Error: Failed to render image.");
         QSKIP("Aborted due to errors.", SkipSingle);
@@ -258,21 +257,21 @@ void tst_Lancelot::runTestSuite()
 }
 
 
-ImageItem tst_Lancelot::render(const ImageItem &item)
+ImageItem tst_Lancelot::render(const ImageItem &item, GraphicsEngine engine, QImage::Format format)
 {
     ImageItem res = item;
     res.imageChecksums.clear();
     res.image = QImage();
-    QString filePath = scriptsDir + item.scriptName;
-    QStringList script = scripts.value(item.scriptName);
+    QString filePath = scriptsDir + item.itemName;
+    QStringList script = scripts.value(item.itemName);
 
-    if (item.engine == ImageItem::Raster) {
-        QImage img(800, 800, item.renderFormat);
+    if (engine == Raster) {
+        QImage img(800, 800, format);
         paint(&img, script, QFileInfo(filePath).absoluteFilePath()); // eh yuck (filePath stuff)
         res.image = img;
         res.imageChecksums.append(ImageItem::computeChecksum(img));
 #ifndef QT_NO_OPENGL
-    } else if (item.engine == ImageItem::OpenGL) {
+    } else if (engine == OpenGL) {
         QGLWidget glWidget;
         if (glWidget.isValid()) {
             glWidget.makeCurrent();
@@ -281,7 +280,7 @@ ImageItem tst_Lancelot::render(const ImageItem &item)
             fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
             QGLFramebufferObject fbo(800, 800, fboFormat);
             paint(&fbo, script, QFileInfo(filePath).absoluteFilePath()); // eh yuck (filePath stuff)
-            res.image = fbo.toImage().convertToFormat(item.renderFormat);
+            res.image = fbo.toImage().convertToFormat(format);
             res.imageChecksums.append(ImageItem::computeChecksum(res.image));
         }
 #endif
-- 
cgit v0.12
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\n";
-
-    out << "\n";
-    out << "| " << key << " | " << plat.value(key) << " |  Clear all baselines (They will be recreated by the next run)\n\n";
-
-    out << " \n"
-           "\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"
-           "| Script\n"
-           " | Baseline\n"
-           " | Rendered\n"
-           " | Comparison (diffs are RED)\n"
-           " | Info/Action\n"
-           " |  ";
-}
-
-
-void HTMLPage::writeFooter()
-{
-    out << "
 \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| " << item.itemName << "\n";
-    QStringList images = QStringList() << baseline << rendered << compared;
-    foreach(const QString& img, images)
-        out << " | \n";
-
-    out << " | \n"
-        << " \n";
-    out << "Replace baseline with rendered\n"
-        << " Blacklist this item\n"
-        << " View\n"
-        << " |  " << imageItems.at(i).itemName << " | N/A | N/A | N/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 << "| " << key << " | " << plat.value(key) << " | 
\n";
+    out << "
\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"
+           "| Item\n"
+           " | Baseline\n"
+           " | Rendered\n"
+           " | Comparison (diffs are RED)\n"
+           " | Info/Action\n"
+           " | 
\n\n";
+
+    foreach (const ImageItem &item, list) {
+        out << "\n";
+        out << "| " << item.itemName << "\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 << " | view\n"
+                << " | n/a\n"
+                << " | ";
+            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";
+        }
+        out << " | 
\n\n";
+    }
+
+    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"
+        << " \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:Mismatch reported\n"
+        << " Replace baseline with rendered\n"
+        << " Blacklist this item\n"
+        << " Inspect\n"
+        << " | 
" << 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
From 017a5cfc6462317e6cca0f04a8339e2575bb9130 Mon Sep 17 00:00:00 2001
From: aavit 
Date: Wed, 15 Dec 2010 21:12:52 +0100
Subject: Satisfy the maketestselftest autotst
---
 tests/auto/other.pro | 1 +
 1 file changed, 1 insertion(+)
diff --git a/tests/auto/other.pro b/tests/auto/other.pro
index d1a7a86..512cd25 100644
--- a/tests/auto/other.pro
+++ b/tests/auto/other.pro
@@ -4,6 +4,7 @@
 TEMPLATE=subdirs
 SUBDIRS=\
 #           exceptionsafety_objects \ shouldn't enable it
+#           baselineexample \ Just an example demonstrating qbaselinetest usage
            lancelot \
            qaccessibility \
            qalgorithms \
-- 
cgit v0.12