Skip to content

Commit

Permalink
Added a helper class for unit tests that will compare an image with a…
Browse files Browse the repository at this point in the history
… maprender product and return true or false based on their equiality or on the time it takes to render the output. It will also produce a difference image so that dissimilar areas in the two images can easily be identified. Lastly it generates a simple report as an html snippet for easy inclusion of results in a test report in a web browser.

git-svn-id: http://svn.osgeo.org/qgis/trunk/qgis@8016 c8812cc2-4d05-0410-92ff-de0c093fc19c
  • Loading branch information
timlinux committed Jan 21, 2008
1 parent 6e4cfb4 commit 0290817
Show file tree
Hide file tree
Showing 2 changed files with 256 additions and 0 deletions.
191 changes: 191 additions & 0 deletions tests/src/core/qgsrenderchecker.cpp
@@ -0,0 +1,191 @@
/***************************************************************************
qgsrenderchecker.cpp
--------------------------------------
Date : 18 Jan 2008
Copyright : (C) 2008 by Tim Sutton
Email : tim @ linfiniti.com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsrenderchecker.h"

#include <QDir>
#include <QColor>
#include <QPainter>
#include <QImage>
#include <QTime>


QgsRenderChecker::QgsRenderChecker( ) :
mReport(""),
mExpectedImageFile(""),
mMismatchCount(0),
mMatchTarget(0),
mElapsedTime(0),
mElapsedTimeTarget(0),
mpMapRenderer(NULL)
{

}
bool QgsRenderChecker::runTest( QString theTestName )
{
if (mExpectedImageFile.isEmpty())
{
qDebug("QgsRenderChecker::runTest failed - Expected Image File not set.");
mReport= "<table>"
"<tr><td>Test Result:</td><td>Expected Result:</td></tr>\n"
"<tr><td>Nothing rendered</td>\n<td>Failed because Expected "
"Image File not set.</td></tr></table>\n";
return false;
}
//
// Load the expected result pixmap
//
QImage myExpectedImage (mExpectedImageFile);
mMatchTarget = myExpectedImage.width() * myExpectedImage.height();
//
// Now render our layers onto a pixmap
//
QImage myImage( myExpectedImage.width() , myExpectedImage.height(), QImage::Format_RGB32 );
QImage myDifferenceImage( myExpectedImage.width() , myExpectedImage.height(), QImage::Format_RGB32);
QString myResultDiffImage = QDir::tempPath() + QDir::separator() + theTestName + "_result_diff.png";
myImage.fill ( QColor ( "#98dbf9" ).pixel() );
myDifferenceImage.fill ( QColor ( "#98dbf9" ).pixel() );
QPainter myPainter( &myImage );
mpMapRenderer->setOutputSize( QSize ( myExpectedImage.width(),myExpectedImage.height() ),72 );
QTime myTime;
myTime.start();
mpMapRenderer->render( &myPainter );
mElapsedTime = myTime.elapsed();
myPainter.end();
//
// Save the pixmap to disk so the user can make a
// visual assessment if needed
//
QString myResultImage = QDir::tempPath() + QDir::separator() + theTestName + "_result.png";
myImage.save (myResultImage);
//
// Set pixel count score and target
//
mMatchTarget = myExpectedImage.width() * myExpectedImage.height();
int myPixelCount = myImage.width() * myImage.height();
//
// Set the report with the result
//
mReport= "<table>";
mReport += "<tr><td colspan=2>";
mReport += "Test image and result image for " + theTestName + "<br>"
"Expected size: " + QString::number(myExpectedImage.width()).toLocal8Bit() + "w x " +
QString::number(myExpectedImage.width()).toLocal8Bit() + "h (" +
QString::number(mMatchTarget).toLocal8Bit() + " pixels)<br>"
"Actual size: " + QString::number(myImage.width()).toLocal8Bit() + "w x " +
QString::number(myImage.width()).toLocal8Bit() + "h (" +
QString::number(myPixelCount).toLocal8Bit() + " pixels)";
mReport += "</td></tr>";
mReport += "<tr><td colspan = 2>\n";
mReport += "Expected Duration : <= " + QString::number(mElapsedTimeTarget) +
"ms (0 indicates not specified)<br>";
mReport += "Actual Duration : " + QString::number(mElapsedTime) + "ms<br>";
QString myImagesString= "</td></tr>"
"<tr><td>Test Result:</td><td>Expected Result:</td><td>Difference (all blue is good, any red is bad)</td></tr>\n"
"<tr><td><img src=\"" +
myResultImage +
"\"></td>\n<td><img src=\"" +
mExpectedImageFile +
"\"></td><td><img src=\"" +
myResultDiffImage +
"\"></td>\n</tr>\n</table>";
//
// Put the same info to debug too
//
qDebug ("Expected size: " + QString::number(myExpectedImage.width()).toLocal8Bit() + + "w x " +
QString::number(myExpectedImage.width()).toLocal8Bit() + + "h");
qDebug ("Actual size: " + QString::number(myImage.width()).toLocal8Bit() + + "w x " +
QString::number(myImage.width()).toLocal8Bit() + + "h");
//
// Now load the renderered image and the expected image
// and then iterate through them counting how many
// dissimilar pixel values there are
//

if (mMatchTarget!= myPixelCount )
{
qDebug ("Test image and result image for " + theTestName + " are different - FAILING!");
mReport += "<tr><td colspan=3>";
mReport += "<font color=red>Expected image and result image for " + theTestName + " are different dimensions - FAILING!</font>";
mReport += "</td></tr>";
mReport += myImagesString;
return false;
}
mMismatchCount = 0;
for (int x = 0; x < myExpectedImage.width(); ++x)
{
for (int y = 0; y < myExpectedImage.height(); ++y)
{
QRgb myExpectedPixel = myExpectedImage.pixel(x,y);
QRgb myActualPixel = myImage.pixel(x,y);
if (myExpectedPixel != myActualPixel)
{
++mMismatchCount;
myDifferenceImage.setPixel(x,y,QColor (255,0,0).pixel());
}
}
}
//
//save the diff image to disk
//
myDifferenceImage.save (myResultDiffImage);

//
// Send match result to debug
//
qDebug (QString::number(mMismatchCount).toLocal8Bit() + "/" +
QString::number(mMatchTarget).toLocal8Bit() +
" pixels mismatched");;

//
// Send match result to report
//
mReport += "<tr><td colspan=3>" +
QString::number(mMismatchCount) + "/" +
QString::number(mMatchTarget) +
" pixels mismatched";
mReport += "</td></tr>";


if ( mMismatchCount==0 )
{
mReport += "<tr><td colspan = 3>\n";
mReport += "Test image and result image for " + theTestName + " are matched<br>";
mReport += "</td></tr>";
if (mElapsedTimeTarget != 0 && mElapsedTimeTarget < mElapsedTime)
{
//test failed because it took too long...
qDebug("Test failed because render step took too long");
mReport += "<tr><td colspan = 3>\n";
mReport += "<font color=red>Test failed because render step took too long</font>";
mReport += "</td></tr>";
mReport += myImagesString;
return false;
}
else
{
mReport += myImagesString;
return true;
}
}
else
{
mReport += "<tr><td colspan = 3>\n";
mReport += "<font color=red>Test image and result image for " + theTestName + " are mismatched</font><br>";
mReport += "</td></tr>";
mReport += myImagesString;
return false;
}
}
65 changes: 65 additions & 0 deletions tests/src/core/qgsrenderchecker.h
@@ -0,0 +1,65 @@
/***************************************************************************
qgsrenderchecker.h - check maprender output against an expected image
--------------------------------------
Date : 18 Jan 2008
Copyright : (C) 2008 by Tim Sutton
email : tim @ linfiniti.com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/* $Id: qgsfield.h 6833 2007-03-24 22:40:10Z wonder $ */

#ifndef QGSRENDERCHECKER_H
#define QGSRENDERCHECKER_H

#include <QString>
#include <qgsmaprender.h>


/** \ingroup UnitTests
* This is a helper class for unit tests that need to
* write an image and compare it to an expected result
* or render time.
*/
class QgsRenderChecker
{
public:

QgsRenderChecker();

//! Destructor
~QgsRenderChecker(){};

QString report() { return mReport; };
float matchPercent() { return static_cast<float>(mMismatchCount) /
static_cast<float>(mMatchTarget) * 100; };
unsigned int mismatchCount() { return mMismatchCount; };
unsigned int matchTarget() { return mMatchTarget; };
//only records time for actual render part
int elapsedTime() { return mElapsedTime; };
void setElapsedTimeTarget(int theTarget) { mElapsedTimeTarget = theTarget; };
void setExpectedImage (QString theImageFileName) { mExpectedImageFile = theImageFileName; };
void setMapRenderer ( QgsMapRender * thepMapRenderer) { mpMapRenderer = thepMapRenderer; };
/**
* @param theTestName - to be used as the basis for writing a file to
* /tmp/theTestName.png
*/
bool runTest( QString theTestName );

private:
QString mReport;
QString mExpectedImageFile;
unsigned int mMismatchCount;
unsigned int mMatchTarget;
int mElapsedTime;
int mElapsedTimeTarget;
QgsMapRender * mpMapRenderer;

}; // class QgsRenderChecker

#endif

0 comments on commit 0290817

Please sign in to comment.