23 #include <QCryptographicHash>
31 mpMapRenderer( NULL ),
33 mRenderedImageFile(
"" ),
34 mExpectedImageFile(
"" ),
37 mElapsedTimeTarget( 0 ),
38 mControlPathPrefix(
"" )
45 QString myDataDir( TEST_DATA_DIR );
46 QString myControlImageDir = myDataDir + QDir::separator() +
"control_images" +
48 return myControlImageDir;
61 myImage.load( theImageFile );
62 QByteArray myByteArray;
63 QBuffer myBuffer( &myByteArray );
64 myImage.save( &myBuffer,
"PNG" );
65 QString myImageString = QString::fromUtf8( myByteArray.toBase64().data() );
66 QCryptographicHash myHash( QCryptographicHash::Md5 );
67 myHash.addData( myImageString.toUtf8() );
68 return myHash.result().toHex().constData();
75 QDir myDirectory = QDir( myControlImageDir );
77 QString myFilename =
"*";
78 myList = myDirectory.entryList( QStringList( myFilename ),
79 QDir::Files | QDir::NoSymLinks );
84 QString myImageHash =
imageToHash( theDiffImageFile );
87 for (
int i = 0; i < myList.size(); ++i )
89 QString myFile = myList.at( i );
91 "Checking if " + myFile +
" is a known anomaly.";
94 + QDir::separator() + myFile );
95 QString myHashMessage = QString(
96 "Checking if anomaly %1 (hash %2)<br>" )
98 .arg( myAnomalyHash );
99 myHashMessage += QString(
" matches %1 (hash %2)" )
100 .arg( theDiffImageFile )
103 QString myMeasureMessage =
"<DartMeasurement name=\"Anomaly check"
104 "\" type=\"text/text\">" + myHashMessage +
105 "</DartMeasurement>";
106 qDebug() << myMeasureMessage;
107 mReport +=
"<tr><td colspan=3>" + myHashMessage +
"</td></tr>";
108 if ( myImageHash == myAnomalyHash )
110 mReport +=
"<tr><td colspan=3>"
111 "Anomaly found! " + myFile;
116 mReport +=
"<tr><td colspan=3>"
117 "No anomaly found! ";
123 unsigned int theMismatchCount )
127 qDebug(
"QgsRenderChecker::runTest failed - Expected Image File not set." );
129 "<tr><td>Test Result:</td><td>Expected Result:</td></tr>\n"
130 "<tr><td>Nothing rendered</td>\n<td>Failed because Expected "
131 "Image File not set.</td></tr></table>\n";
138 mMatchTarget = myExpectedImage.width() * myExpectedImage.height();
142 QImage myImage( myExpectedImage.width(),
143 myExpectedImage.height(),
144 QImage::Format_RGB32 );
145 myImage.setDotsPerMeterX( myExpectedImage.dotsPerMeterX() );
146 myImage.setDotsPerMeterY( myExpectedImage.dotsPerMeterY() );
147 myImage.fill( qRgb( 152, 219, 249 ) );
148 QPainter myPainter( &myImage );
149 myPainter.setRenderHint( QPainter::Antialiasing );
151 myExpectedImage.width(),
152 myExpectedImage.height() ),
153 myExpectedImage.logicalDpiX() );
164 theTestName +
"_result.png";
169 QFile wldFile( QDir::tempPath() + QDir::separator() + theTestName +
"_result.wld" );
170 if ( wldFile.open( QIODevice::WriteOnly ) )
174 QTextStream stream( &wldFile );
175 stream << QString(
"%1\r\n0 \r\n0 \r\n%2\r\n%3\r\n%4\r\n" )
187 unsigned int theMismatchCount,
188 QString theRenderedImageFile )
192 qDebug(
"QgsRenderChecker::runTest failed - Expected Image (control) File not set." );
194 "<tr><td>Test Result:</td><td>Expected Result:</td></tr>\n"
195 "<tr><td>Nothing rendered</td>\n<td>Failed because Expected "
196 "Image File not set.</td></tr></table>\n";
199 if ( ! theRenderedImageFile.isEmpty() )
205 qDebug(
"QgsRenderChecker::runTest failed - Rendered Image File not set." );
207 "<tr><td>Test Result:</td><td>Expected Result:</td></tr>\n"
208 "<tr><td>Nothing rendered</td>\n<td>Failed because Rendered "
209 "Image File not set.</td></tr></table>\n";
217 QImage myDifferenceImage( myExpectedImage.width(),
218 myExpectedImage.height(),
219 QImage::Format_RGB32 );
220 QString myDiffImageFile = QDir::tempPath() + QDir::separator() +
222 theTestName +
"_result_diff.png";
223 myDifferenceImage.fill( qRgb( 152, 219, 249 ) );
228 mMatchTarget = myExpectedImage.width() * myExpectedImage.height();
229 unsigned int myPixelCount = myResultImage.width() * myResultImage.height();
234 mReport +=
"<tr><td colspan=2>";
235 mReport +=
"Test image and result image for " + theTestName +
"<br>"
236 "Expected size: " + QString::number( myExpectedImage.width() ).toLocal8Bit() +
"w x " +
237 QString::number( myExpectedImage.height() ).toLocal8Bit() +
"h (" +
238 QString::number(
mMatchTarget ).toLocal8Bit() +
" pixels)<br>"
239 "Actual size: " + QString::number( myResultImage.width() ).toLocal8Bit() +
"w x " +
240 QString::number( myResultImage.height() ).toLocal8Bit() +
"h (" +
241 QString::number( myPixelCount ).toLocal8Bit() +
" pixels)";
243 mReport +=
"<tr><td colspan = 2>\n";
245 "ms (0 indicates not specified)<br>";
251 if ( ! myExpectedImage.isNull() )
253 imgWidth = qMin( myExpectedImage.width(), imgWidth );
254 imgHeight = myExpectedImage.height() * imgWidth / myExpectedImage.width();
256 QString myImagesString =
"</td></tr>"
257 "<tr><td>Test Result:</td><td>Expected Result:</td><td>Difference (all blue is good, any red is bad)</td></tr>\n"
258 "<tr><td><img width=" + QString::number( imgWidth ) +
259 " height=" + QString::number( imgHeight ) +
262 "\"></td>\n<td><img width=" + QString::number( imgWidth ) +
263 " height=" + QString::number( imgHeight ) +
266 "\"></td>\n<td><img width=" + QString::number( imgWidth ) +
267 " height=" + QString::number( imgHeight ) +
270 "\"></td>\n</tr>\n</table>";
274 QString myDashMessage =
"<DartMeasurementFile name=\"Rendered Image " + theTestName +
"\""
276 "</DartMeasurementFile>\n"
277 "<DartMeasurementFile name=\"Expected Image " + theTestName +
"\" type=\"image/png\">" +
279 "<DartMeasurementFile name=\"Difference Image " + theTestName +
"\" type=\"image/png\">" +
280 myDiffImageFile +
"</DartMeasurementFile>\n";
281 qDebug( ) << myDashMessage;
287 qDebug(
"Expected size: %dw x %dh", myExpectedImage.width(), myExpectedImage.height() );
288 qDebug(
"Actual size: %dw x %dh", myResultImage.width(), myResultImage.height() );
292 qDebug(
"Test image and result image for %s are different - FAILING!", theTestName.toLocal8Bit().constData() );
293 mReport +=
"<tr><td colspan=3>";
294 mReport +=
"<font color=red>Expected image and result image for " + theTestName +
" are different dimensions - FAILING!</font>";
295 mReport +=
"</td></tr>";
296 mReport += myImagesString;
307 for (
int x = 0; x < myExpectedImage.width(); ++x )
309 for (
int y = 0; y < myExpectedImage.height(); ++y )
311 QRgb myExpectedPixel = myExpectedImage.pixel( x, y );
312 QRgb myActualPixel = myResultImage.pixel( x, y );
315 if ( myExpectedPixel != myActualPixel )
318 myDifferenceImage.setPixel( x, y, qRgb( 255, 0, 0 ) );
323 if ( qAbs( qRed( myExpectedPixel ) - qRed( myActualPixel ) ) > colorTolerance ||
324 qAbs( qGreen( myExpectedPixel ) - qGreen( myActualPixel ) ) > colorTolerance ||
325 qAbs( qBlue( myExpectedPixel ) - qBlue( myActualPixel ) ) > colorTolerance ||
326 qAbs( qAlpha( myExpectedPixel ) - qAlpha( myActualPixel ) ) > colorTolerance )
329 myDifferenceImage.setPixel( x, y, qRgb( 255, 0, 0 ) );
337 myDifferenceImage.save( myDiffImageFile );
347 mReport +=
"<tr><td colspan=3>" +
350 " pixels mismatched (allowed threshold: " +
351 QString::number( theMismatchCount ) +
352 ", allowed color component tolerance: " +
359 myDashMessage =
"<DartMeasurement name=\"Mismatch Count "
360 "\" type=\"numeric/integer\">" +
363 "</DartMeasurement>";
364 qDebug( ) << myDashMessage;
368 if ( myAnomalyMatchFlag )
370 mReport +=
"<tr><td colspan=3>"
371 "Difference image matched a known anomaly - passing test! "
377 QString myMessage =
"Difference image did not match any known anomaly.";
378 mReport +=
"<tr><td colspan=3>"
380 QString myMeasureMessage =
"<DartMeasurement name=\"No Anomalies Match"
381 "\" type=\"text/text\">" + myMessage +
382 " If you feel the difference image should be considered an anomaly "
383 "you can do something like this\n"
384 "cp " + myDiffImageFile +
" ../tests/testdata/control_images/" + theTestName +
385 "/<imagename>.{wld,png}"
386 "</DartMeasurement>";
387 qDebug() << myMeasureMessage;
392 mReport +=
"<tr><td colspan = 3>\n";
393 mReport +=
"Test image and result image for " + theTestName +
" are matched<br>";
398 qDebug(
"Test failed because render step took too long" );
399 mReport +=
"<tr><td colspan = 3>\n";
400 mReport +=
"<font color=red>Test failed because render step took too long</font>";
413 mReport +=
"<tr><td colspan = 3>\n";
414 mReport +=
"<font color=red>Test image and result image for " + theTestName +
" are mismatched</font><br>";
QgsMapRenderer * mpMapRenderer
A rectangle specified with double values.
void render(QPainter *painter, double *forceWidthScale=0)
starts rendering @ param forceWidthScale Force a specific scale factor for line widths and marker siz...
double yMaximum() const
Get the y maximum value (top side of rectangle)
QgsRectangle extent() const
returns current extent
QString qgsDoubleToString(const double &a)
bool runTest(QString theTestName, unsigned int theMismatchCount=0)
Test using renderer to generate the image to be compared.
unsigned int mMismatchCount
QString controlImagePath() const
double mapUnitsPerPixel() const
QString imageToHash(QString theImageFile)
Get an md5 hash that uniquely identifies an image.
unsigned int mMatchTarget
void setOutputSize(QSize size, int dpi)
QString mControlPathPrefix
QString mRenderedImageFile
unsigned int mColorTolerance
bool isKnownAnomaly(QString theDiffImageFile)
Get a list of all the anomalies.
QString mExpectedImageFile
void setControlName(const QString theName)
Base directory name for the control image (with control image path suffixed) the path to the image wi...
bool compareImages(QString theTestName, unsigned int theMismatchCount=0, QString theRenderedImageFile="")
Test using two arbitary images (map renderer will not be used)
double xMinimum() const
Get the x minimum value (left side of rectangle)