From 0290817030117a57b38dfe128fd02cb42073821a Mon Sep 17 00:00:00 2001 From: timlinux Date: Mon, 21 Jan 2008 13:15:01 +0000 Subject: [PATCH] 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 --- tests/src/core/qgsrenderchecker.cpp | 191 ++++++++++++++++++++++++++++ tests/src/core/qgsrenderchecker.h | 65 ++++++++++ 2 files changed, 256 insertions(+) create mode 100644 tests/src/core/qgsrenderchecker.cpp create mode 100644 tests/src/core/qgsrenderchecker.h diff --git a/tests/src/core/qgsrenderchecker.cpp b/tests/src/core/qgsrenderchecker.cpp new file mode 100644 index 00000000000..6f07e38e2c4 --- /dev/null +++ b/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 +#include +#include +#include +#include + + +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= "" + "\n" + "\n
Test Result:Expected Result:
Nothing renderedFailed because Expected " + "Image File not set.
\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= ""; + mReport += ""; + mReport += "" + "\n" + "\n\n\n
"; + mReport += "Test image and result image for " + theTestName + "
" + "Expected size: " + QString::number(myExpectedImage.width()).toLocal8Bit() + "w x " + + QString::number(myExpectedImage.width()).toLocal8Bit() + "h (" + + QString::number(mMatchTarget).toLocal8Bit() + " pixels)
" + "Actual size: " + QString::number(myImage.width()).toLocal8Bit() + "w x " + + QString::number(myImage.width()).toLocal8Bit() + "h (" + + QString::number(myPixelCount).toLocal8Bit() + " pixels)"; + mReport += "
\n"; + mReport += "Expected Duration : <= " + QString::number(mElapsedTimeTarget) + + "ms (0 indicates not specified)
"; + mReport += "Actual Duration : " + QString::number(mElapsedTime) + "ms
"; + QString myImagesString= "
Test Result:Expected Result:Difference (all blue is good, any red is bad)
"; + // + // 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 += ""; + mReport += "Expected image and result image for " + theTestName + " are different dimensions - FAILING!"; + mReport += ""; + 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 += "" + + QString::number(mMismatchCount) + "/" + + QString::number(mMatchTarget) + + " pixels mismatched"; + mReport += ""; + + + if ( mMismatchCount==0 ) + { + mReport += "\n"; + mReport += "Test image and result image for " + theTestName + " are matched
"; + mReport += ""; + if (mElapsedTimeTarget != 0 && mElapsedTimeTarget < mElapsedTime) + { + //test failed because it took too long... + qDebug("Test failed because render step took too long"); + mReport += "\n"; + mReport += "Test failed because render step took too long"; + mReport += ""; + mReport += myImagesString; + return false; + } + else + { + mReport += myImagesString; + return true; + } + } + else + { + mReport += "\n"; + mReport += "Test image and result image for " + theTestName + " are mismatched
"; + mReport += ""; + mReport += myImagesString; + return false; + } +} diff --git a/tests/src/core/qgsrenderchecker.h b/tests/src/core/qgsrenderchecker.h new file mode 100644 index 00000000000..cbe3c25f74c --- /dev/null +++ b/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 +#include + + +/** \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(mMismatchCount) / + static_cast(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