QGIS API Documentation  3.23.0-Master (7c4a6de034)
qgsmultirenderchecker.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmultirenderchecker.cpp
3  --------------------------------------
4  Date : 6.11.2014
5  Copyright : (C) 2014 Matthias Kuhn
6  Email : matthias at opengis dot ch
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsmultirenderchecker.h"
17 #include "qgslayout.h"
18 #include <QDebug>
19 
20 void QgsMultiRenderChecker::setControlName( const QString &name )
21 {
22  mControlName = name;
23 }
24 
25 void QgsMultiRenderChecker::setControlPathPrefix( const QString &prefix )
26 {
27  mControlPathPrefix = prefix;
28 }
29 
31 {
32  mMapSettings = mapSettings;
33 }
34 
35 bool QgsMultiRenderChecker::runTest( const QString &testName, unsigned int mismatchCount )
36 {
37  bool successful = false;
38 
39  const QString baseDir = controlImagePath();
40 
41  QStringList subDirs = QDir( baseDir ).entryList( QDir::Dirs | QDir::NoDotAndDotDot );
42 
43  if ( subDirs.isEmpty() )
44  {
45  subDirs << QString();
46  }
47 
48  QVector<QgsDartMeasurement> dartMeasurements;
49 
50  for ( const QString &suffix : std::as_const( subDirs ) )
51  {
52  if ( subDirs.count() > 1 )
53  {
54  qDebug() << "Checking subdir " << suffix;
55  }
56  bool result;
57  QgsRenderChecker checker;
58  checker.enableDashBuffering( true );
59  checker.setColorTolerance( mColorTolerance );
60  checker.setSizeTolerance( mMaxSizeDifferenceX, mMaxSizeDifferenceY );
61  checker.setControlPathPrefix( mControlPathPrefix );
62  checker.setControlPathSuffix( suffix );
63  checker.setControlName( mControlName );
64  checker.setMapSettings( mMapSettings );
65 
66  if ( !mRenderedImage.isNull() )
67  {
68  checker.setRenderedImage( mRenderedImage );
69  result = checker.compareImages( testName, mismatchCount, mRenderedImage );
70  }
71  else
72  {
73  result = checker.runTest( testName, mismatchCount );
74  mRenderedImage = checker.renderedImage();
75  }
76 
77  successful |= result;
78 
79  dartMeasurements << checker.dartMeasurements();
80 
81  mReport += checker.report();
82  }
83 
84  if ( !successful )
85  {
86  const auto constDartMeasurements = dartMeasurements;
87  for ( const QgsDartMeasurement &measurement : constDartMeasurements )
88  measurement.send();
89 
90  QgsDartMeasurement msg( QStringLiteral( "Image not accepted by test" ), QgsDartMeasurement::Text, "This may be caused because the test is supposed to fail or rendering inconsistencies."
91  "If this is a rendering inconsistency, please add another control image folder, add an anomaly image or increase the color tolerance." );
92  msg.send();
93  }
94 
95  return successful;
96 }
97 
99 {
100  QString myDataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
101  QString myControlImageDir = myDataDir + QDir::separator() + "control_images" +
102  QDir::separator() + mControlPathPrefix + QDir::separator() + mControlName + QDir::separator();
103  return myControlImageDir;
104 }
105 
106 //
107 // QgsLayoutChecker
108 //
109 
111 
112 QgsLayoutChecker::QgsLayoutChecker( const QString &testName, QgsLayout *layout )
113  : mTestName( testName )
114  , mLayout( layout )
115  , mSize( 1122, 794 )
116  , mDotsPerMeter( 96 / 25.4 * 1000 )
117 {
118  // Qt has some slight render inconsistencies on the whole image sometimes
119  setColorTolerance( 5 );
120 }
121 
122 bool QgsLayoutChecker::testLayout( QString &checkedReport, int page, int pixelDiff, bool createReferenceImage )
123 {
124 #ifdef QT_NO_PRINTER
125  return false;
126 #else
127  if ( !mLayout )
128  {
129  return false;
130  }
131 
132  setControlName( "expected_" + mTestName );
133 
134 
135  if ( createReferenceImage )
136  {
137  //fake mode to generate expected image
138  //assume 96 dpi
139 
140 
141  QImage _outputImage( mSize, QImage::Format_RGB32 );
142  _outputImage.setDotsPerMeterX( 96 / 25.4 * 1000 );
143  _outputImage.setDotsPerMeterY( 96 / 25.4 * 1000 );
144  QPainter _p( &_outputImage );
145  QgsLayoutExporter _exporter( mLayout );
146  _exporter.renderPage( &_p, page );
147  _p.end();
148 
149  if ( ! QDir( controlImagePath() ).exists() )
150  {
151  QDir().mkdir( controlImagePath() );
152  }
153  _outputImage.save( controlImagePath() + QDir::separator() + "expected_" + mTestName + ".png", "PNG" );
154  qDebug( ) << "Reference image saved to : " + controlImagePath() + QDir::separator() + "expected_" + mTestName + ".png";
155 
156  }
157 
158  QImage outputImage( mSize, QImage::Format_RGB32 );
159  outputImage.setDotsPerMeterX( mDotsPerMeter );
160  outputImage.setDotsPerMeterY( mDotsPerMeter );
161  drawBackground( &outputImage );
162  QPainter p( &outputImage );
163  QgsLayoutExporter exporter( mLayout );
164  exporter.renderPage( &p, page );
165  p.end();
166 
167  QString renderedFilePath = QDir::tempPath() + '/' + QFileInfo( mTestName ).baseName() + "_rendered.png";
168  outputImage.save( renderedFilePath, "PNG" );
169 
170  setRenderedImage( renderedFilePath );
171 
172  bool testResult = runTest( mTestName, pixelDiff );
173 
174  checkedReport += report();
175 
176  return testResult;
177 #endif // QT_NO_PRINTER
178 }
179 
180 
181 
Handles rendering and exports of layouts to various formats.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:51
The QgsMapSettings class contains configuration for rendering of the map.
bool runTest(const QString &testName, unsigned int mismatchCount=0)
Test using renderer to generate the image to be compared.
void setControlName(const QString &name)
Base directory name for the control image (with control image path suffixed) the path to the image wi...
QString controlImagePath() const
Returns the path to the control images.
void setControlPathPrefix(const QString &prefix)
void setColorTolerance(unsigned int colorTolerance)
Set tolerance for color components used by runTest() Default value is 0.
void setMapSettings(const QgsMapSettings &mapSettings)
Set the map settings to use to render the image.
This is a helper class for unit tests that need to write an image and compare it to an expected resul...
void setControlName(const QString &name)
Sets the base directory name for the control image (with control image path suffixed).
void setMapSettings(const QgsMapSettings &mapSettings)
void setControlPathSuffix(const QString &name)
QString renderedImage() const
Returns the path of the rendered image generated by the test.
QVector< QgsDartMeasurement > dartMeasurements() const
Gets access to buffered dash messages.
void setControlPathPrefix(const QString &name)
Sets the path prefix where the control images are kept.
void setRenderedImage(const QString &imageFileName)
Sets the file name of the rendered image generated by the test.
bool runTest(const QString &testName, unsigned int mismatchCount=0)
Test using renderer to generate the image to be compared.
void setSizeTolerance(int xTolerance, int yTolerance)
Sets the largest allowable difference in size between the rendered and the expected image.
void enableDashBuffering(bool enable)
Call this to enable internal buffering of dash messages.
void setColorTolerance(unsigned int colorTolerance)
Set tolerance for color components used by runTest() and compareImages().
QString report()
Returns the HTML report describing the results of the test run.
bool compareImages(const QString &testName, unsigned int mismatchCount=0, const QString &renderedImageFile=QString())
Test using two arbitrary images (map renderer will not be used)