25 #include <QCryptographicHash>
34 mRenderedImageFile(
"" ),
35 mExpectedImageFile(
"" ),
38 mElapsedTimeTarget( 0 ),
39 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();
85 QDir myDirectory = QDir( myControlImageDir );
87 QString myFilename =
"*";
88 myList = myDirectory.entryList( QStringList( myFilename ),
89 QDir::Files | QDir::NoSymLinks );
94 QString myImageHash =
imageToHash( theDiffImageFile );
97 for (
int i = 0; i < myList.size(); ++i )
99 QString myFile = myList.at( i );
100 mReport +=
"<tr><td colspan=3>"
101 "Checking if " + myFile +
" is a known anomaly.";
104 + QDir::separator() + myFile );
105 QString myHashMessage = QString(
106 "Checking if anomaly %1 (hash %2)<br>" )
108 .arg( myAnomalyHash );
109 myHashMessage += QString(
" matches %1 (hash %2)" )
110 .arg( theDiffImageFile )
113 QString myMeasureMessage =
"<DartMeasurement name=\"Anomaly check"
114 "\" type=\"text/text\">" + myHashMessage +
115 "</DartMeasurement>";
116 qDebug() << myMeasureMessage;
117 mReport +=
"<tr><td colspan=3>" + myHashMessage +
"</td></tr>";
118 if ( myImageHash == myAnomalyHash )
120 mReport +=
"<tr><td colspan=3>"
121 "Anomaly found! " + myFile;
126 mReport +=
"<tr><td colspan=3>"
127 "No anomaly found! ";
133 unsigned int theMismatchCount )
137 qDebug(
"QgsRenderChecker::runTest failed - Expected Image File not set." );
139 "<tr><td>Test Result:</td><td>Expected Result:</td></tr>\n"
140 "<tr><td>Nothing rendered</td>\n<td>Failed because Expected "
141 "Image File not set.</td></tr></table>\n";
148 mMatchTarget = myExpectedImage.width() * myExpectedImage.height();
172 theTestName +
"_result.png";
174 myImage.setDotsPerMeterX( myExpectedImage.dotsPerMeterX() );
175 myImage.setDotsPerMeterY( myExpectedImage.dotsPerMeterY() );
180 QFile wldFile( QDir::tempPath() + QDir::separator() + theTestName +
"_result.wld" );
181 if ( wldFile.open( QIODevice::WriteOnly ) )
185 QTextStream stream( &wldFile );
186 stream << QString(
"%1\r\n0 \r\n0 \r\n%2\r\n%3\r\n%4\r\n" )
198 unsigned int theMismatchCount,
199 QString theRenderedImageFile )
203 qDebug(
"QgsRenderChecker::runTest failed - Expected Image (control) File not set." );
205 "<tr><td>Test Result:</td><td>Expected Result:</td></tr>\n"
206 "<tr><td>Nothing rendered</td>\n<td>Failed because Expected "
207 "Image File not set.</td></tr></table>\n";
210 if ( ! theRenderedImageFile.isEmpty() )
216 qDebug(
"QgsRenderChecker::runTest failed - Rendered Image File not set." );
218 "<tr><td>Test Result:</td><td>Expected Result:</td></tr>\n"
219 "<tr><td>Nothing rendered</td>\n<td>Failed because Rendered "
220 "Image File not set.</td></tr></table>\n";
228 QImage myDifferenceImage( myExpectedImage.width(),
229 myExpectedImage.height(),
230 QImage::Format_RGB32 );
231 QString myDiffImageFile = QDir::tempPath() + QDir::separator() +
233 theTestName +
"_result_diff.png";
234 myDifferenceImage.fill( qRgb( 152, 219, 249 ) );
239 mMatchTarget = myExpectedImage.width() * myExpectedImage.height();
240 unsigned int myPixelCount = myResultImage.width() * myResultImage.height();
245 mReport +=
"<tr><td colspan=2>";
246 mReport +=
"Test image and result image for " + theTestName +
"<br>"
247 "Expected size: " + QString::number( myExpectedImage.width() ).toLocal8Bit() +
"w x " +
248 QString::number( myExpectedImage.height() ).toLocal8Bit() +
"h (" +
249 QString::number(
mMatchTarget ).toLocal8Bit() +
" pixels)<br>"
250 "Actual size: " + QString::number( myResultImage.width() ).toLocal8Bit() +
"w x " +
251 QString::number( myResultImage.height() ).toLocal8Bit() +
"h (" +
252 QString::number( myPixelCount ).toLocal8Bit() +
" pixels)";
254 mReport +=
"<tr><td colspan = 2>\n";
256 "ms (0 indicates not specified)<br>";
262 if ( ! myExpectedImage.isNull() )
264 imgWidth = qMin( myExpectedImage.width(), imgWidth );
265 imgHeight = myExpectedImage.height() * imgWidth / myExpectedImage.width();
267 QString myImagesString =
"</td></tr>"
268 "<tr><td>Test Result:</td><td>Expected Result:</td><td>Difference (all blue is good, any red is bad)</td></tr>\n"
269 "<tr><td><img width=" + QString::number( imgWidth ) +
270 " height=" + QString::number( imgHeight ) +
273 "\"></td>\n<td><img width=" + QString::number( imgWidth ) +
274 " height=" + QString::number( imgHeight ) +
277 "\"></td>\n<td><img width=" + QString::number( imgWidth ) +
278 " height=" + QString::number( imgHeight ) +
281 "\"></td>\n</tr>\n</table>";
285 QString myDashMessage =
"<DartMeasurementFile name=\"Rendered Image " + theTestName +
"\""
287 "</DartMeasurementFile>\n"
288 "<DartMeasurementFile name=\"Expected Image " + theTestName +
"\" type=\"image/png\">" +
290 "<DartMeasurementFile name=\"Difference Image " + theTestName +
"\" type=\"image/png\">" +
291 myDiffImageFile +
"</DartMeasurementFile>\n";
292 qDebug( ) << myDashMessage;
298 qDebug(
"Expected size: %dw x %dh", myExpectedImage.width(), myExpectedImage.height() );
299 qDebug(
"Actual size: %dw x %dh", myResultImage.width(), myResultImage.height() );
303 qDebug(
"Test image and result image for %s are different - FAILING!", theTestName.toLocal8Bit().constData() );
304 mReport +=
"<tr><td colspan=3>";
305 mReport +=
"<font color=red>Expected image and result image for " + theTestName +
" are different dimensions - FAILING!</font>";
306 mReport +=
"</td></tr>";
307 mReport += myImagesString;
318 for (
int x = 0; x < myExpectedImage.width(); ++x )
320 for (
int y = 0; y < myExpectedImage.height(); ++y )
322 QRgb myExpectedPixel = myExpectedImage.pixel( x, y );
323 QRgb myActualPixel = myResultImage.pixel( x, y );
326 if ( myExpectedPixel != myActualPixel )
329 myDifferenceImage.setPixel( x, y, qRgb( 255, 0, 0 ) );
334 if ( qAbs( qRed( myExpectedPixel ) - qRed( myActualPixel ) ) > colorTolerance ||
335 qAbs( qGreen( myExpectedPixel ) - qGreen( myActualPixel ) ) > colorTolerance ||
336 qAbs( qBlue( myExpectedPixel ) - qBlue( myActualPixel ) ) > colorTolerance ||
337 qAbs( qAlpha( myExpectedPixel ) - qAlpha( myActualPixel ) ) > colorTolerance )
340 myDifferenceImage.setPixel( x, y, qRgb( 255, 0, 0 ) );
348 myDifferenceImage.save( myDiffImageFile );
358 mReport +=
"<tr><td colspan=3>" +
361 " pixels mismatched (allowed threshold: " +
362 QString::number( theMismatchCount ) +
363 ", allowed color component tolerance: " +
370 myDashMessage =
"<DartMeasurement name=\"Mismatch Count "
371 "\" type=\"numeric/integer\">" +
374 "</DartMeasurement>";
375 qDebug( ) << myDashMessage;
379 if ( myAnomalyMatchFlag )
381 mReport +=
"<tr><td colspan=3>"
382 "Difference image matched a known anomaly - passing test! "
388 QString myMessage =
"Difference image did not match any known anomaly.";
389 mReport +=
"<tr><td colspan=3>"
391 QString myMeasureMessage =
"<DartMeasurement name=\"No Anomalies Match"
392 "\" type=\"text/text\">" + myMessage +
393 " If you feel the difference image should be considered an anomaly "
394 "you can do something like this\n"
395 "cp " + myDiffImageFile +
" ../tests/testdata/control_images/" + theTestName +
396 "/<imagename>.{wld,png}"
397 "</DartMeasurement>";
398 qDebug() << myMeasureMessage;
403 mReport +=
"<tr><td colspan = 3>\n";
404 mReport +=
"Test image and result image for " + theTestName +
" are matched<br>";
409 qDebug(
"Test failed because render step took too long" );
410 mReport +=
"<tr><td colspan = 3>\n";
411 mReport +=
"<font color=red>Test failed because render step took too long</font>";
424 mReport +=
"<tr><td colspan = 3>\n";
425 mReport +=
"<font color=red>Test image and result image for " + theTestName +
" are mismatched</font><br>";
const QgsMapSettings & mapSettings()
bridge to QgsMapSettings
A rectangle specified with double values.
virtual void waitForFinished()
Block until the job has finished.
double yMaximum() const
Get the y maximum value (top side of rectangle)
QString qgsDoubleToString(const double &a)
virtual QImage renderedImage()
Get a preview/resulting image.
Q_DECL_DEPRECATED void setMapRenderer(QgsMapRenderer *thepMapRenderer)
bool runTest(QString theTestName, unsigned int theMismatchCount=0)
Test using renderer to generate the image to be compared.
A non GUI class for rendering a map layer set onto a QPainter.
void setMapSettings(const QgsMapSettings &mapSettings)
void setFlag(Flag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
The QgsMapSettings class contains configuration for rendering of the map.
unsigned int mMismatchCount
QString controlImagePath() const
QString imageToHash(QString theImageFile)
Get an md5 hash that uniquely identifies an image.
Enable anti-aliasin for map rendering.
double mapUnitsPerPixel() const
Return the distance in geographical coordinates that equals to one pixel in the map.
unsigned int mMatchTarget
QString mControlPathPrefix
Job implementation that renders everything sequentially in one thread.
QgsMapSettings mMapSettings
QString mRenderedImageFile
void setBackgroundColor(const QColor &color)
Set the background color of the map.
unsigned int mColorTolerance
bool isKnownAnomaly(QString theDiffImageFile)
Get a list of all the anomalies.
void setOutputSize(const QSize &size)
Set the size of the resulting map image.
QString mExpectedImageFile
QgsRectangle extent() const
Return geographical coordinates of the rectangle that should be rendered.
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)
virtual void start()
Start the rendering job and immediately return.