Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added a helper class for unit tests that will compare an image with a…
… 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
Showing
2 changed files
with
256 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |