QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
Loading...
Searching...
No Matches
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
17#include "qgslayout.h"
18#include "qgslayoutexporter.h"
19#include <QDebug>
20
22{
23 if ( qgetenv( "QGIS_CONTINUOUS_INTEGRATION_RUN" ) == QStringLiteral( "true" ) )
24 mIsCiRun = true;
25}
26
27void QgsMultiRenderChecker::setControlName( const QString &name )
28{
29 mControlName = name;
30}
31
33{
34 mControlPathPrefix = prefix;
35}
36
38{
39 mMapSettings = mapSettings;
40}
41
42bool QgsMultiRenderChecker::runTest( const QString &testName, unsigned int mismatchCount )
43{
44 mResult = false;
45
46 mReport += "<h2>" + testName + "</h2>\n";
47 mMarkdownReport += QStringLiteral( "### %1\n\n" ).arg( testName );
48
49 const QString baseDir = controlImagePath();
50 if ( !QFile::exists( baseDir ) )
51 {
52 qDebug() << "Control image path " << baseDir << " does not exist!";
53 return mResult;
54 }
55
56 QStringList subDirs = QDir( baseDir ).entryList( QDir::Dirs | QDir::NoDotAndDotDot );
57
58 if ( subDirs.isEmpty() )
59 {
60 subDirs << QString();
61 }
62
63 QVector<QgsDartMeasurement> dartMeasurements;
64
65 // we can only report one diff image, so just use the first
66 QString diffImageFile;
67
68 for ( const QString &suffix : std::as_const( subDirs ) )
69 {
70 if ( subDirs.count() > 1 )
71 {
72 qDebug() << "Checking subdir " << suffix;
73 }
74 bool result;
75 QgsRenderChecker checker;
76 checker.enableDashBuffering( true );
77 checker.setColorTolerance( mColorTolerance );
78 checker.setSizeTolerance( mMaxSizeDifferenceX, mMaxSizeDifferenceY );
79 checker.setControlPathPrefix( mControlPathPrefix );
80 checker.setControlPathSuffix( suffix );
81 checker.setControlName( mControlName );
82 checker.setMapSettings( mMapSettings );
83 checker.setExpectFail( mExpectFail );
84
85 if ( !mRenderedImage.isNull() )
86 {
87 checker.setRenderedImage( mRenderedImage );
88 result = checker.compareImages( testName, mismatchCount, mRenderedImage, QgsRenderChecker::Flag::AvoidExportingRenderedImage );
89 }
90 else
91 {
92 result = checker.runTest( testName, mismatchCount, QgsRenderChecker::Flag::AvoidExportingRenderedImage );
93 mRenderedImage = checker.renderedImage();
94 }
95
96 mResult |= result;
97
98 dartMeasurements << checker.dartMeasurements();
99
100 mReport += checker.report( false );
101 if ( subDirs.count() > 1 )
102 mMarkdownReport += QStringLiteral( "* " ) + checker.markdownReport( false );
103 else
104 mMarkdownReport += checker.markdownReport( false );
105
106 if ( !mResult && diffImageFile.isEmpty() )
107 {
108 diffImageFile = checker.mDiffImageFile;
109 }
110 }
111
112 if ( !mResult && !mExpectFail && mIsCiRun )
113 {
114 const auto constDartMeasurements = dartMeasurements;
115 for ( const QgsDartMeasurement &measurement : constDartMeasurements )
116 measurement.send();
117
118 QgsDartMeasurement msg( QStringLiteral( "Image not accepted by test" ), QgsDartMeasurement::Text, "This may be caused because the test is supposed to fail or rendering inconsistencies."
119 "If this is a rendering inconsistency, please add another control image folder, add an anomaly image or increase the color tolerance." );
120 msg.send();
121
122#if DUMP_BASE64_IMAGES
123 QFile fileSource( mRenderedImage );
124 fileSource.open( QIODevice::ReadOnly );
125
126 const QByteArray blob = fileSource.readAll();
127 const QByteArray encoded = blob.toBase64();
128 qDebug() << "Dumping rendered image " << mRenderedImage << " as base64\n";
129 qDebug() << "################################################################";
130 qDebug() << encoded;
131 qDebug() << "################################################################";
132 qDebug() << "End dump";
133#endif
134 }
135
136 if ( !mResult && !mExpectFail )
137 {
138 const QDir reportDir = QgsRenderChecker::testReportDir();
139 if ( !reportDir.exists() )
140 {
141 if ( !QDir().mkpath( reportDir.path() ) )
142 {
143 qDebug() << "!!!!! cannot create " << reportDir.path();
144 }
145 }
146 if ( QFile::exists( mRenderedImage ) )
147 {
148 QFileInfo fi( mRenderedImage );
149 const QString destPath = reportDir.filePath( fi.fileName() );
150 if ( QFile::exists( destPath ) )
151 QFile::remove( destPath );
152
153 if ( !QFile::copy( mRenderedImage, destPath ) )
154 {
155 qDebug() << "!!!!! could not copy " << mRenderedImage << " to " << destPath;
156 }
157 }
158
159 if ( !diffImageFile.isEmpty() && QFile::exists( diffImageFile ) )
160 {
161 QFileInfo fi( diffImageFile );
162 const QString destPath = reportDir.filePath( fi.fileName() );
163 if ( QFile::exists( destPath ) )
164 QFile::remove( destPath );
165
166 if ( !QFile::copy( diffImageFile, destPath ) )
167 {
168 qDebug() << "!!!!! could not copy " << diffImageFile << " to " << destPath;
169 }
170 }
171 }
172
173 return mResult;
174}
175
177{
178 return !mResult ? mReport : QString();
179}
180
182{
183 return !mResult ? mMarkdownReport : QString();
184}
185
187{
188 QString myDataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
189 QString myControlImageDir = myDataDir + QDir::separator() + "control_images" +
190 QDir::separator() + mControlPathPrefix + QDir::separator() + mControlName + QDir::separator();
191 return myControlImageDir;
192}
193
194//
195// QgsLayoutChecker
196//
197
199
200QgsLayoutChecker::QgsLayoutChecker( const QString &testName, QgsLayout *layout )
201 : mTestName( testName )
202 , mLayout( layout )
203 , mSize( 1122, 794 )
204 , mDotsPerMeter( 96 / 25.4 * 1000 )
205{
206 // Qt has some slight render inconsistencies on the whole image sometimes
208}
209
210bool QgsLayoutChecker::testLayout( QString &checkedReport, int page, int pixelDiff, bool createReferenceImage )
211{
212#ifdef QT_NO_PRINTER
213 return false;
214#else
215 if ( !mLayout )
216 {
217 return false;
218 }
219
220 setControlName( "expected_" + mTestName );
221
222
223 if ( createReferenceImage )
224 {
225 //fake mode to generate expected image
226 //assume 96 dpi
227
228
229 QImage _outputImage( mSize, QImage::Format_RGB32 );
230 _outputImage.setDotsPerMeterX( 96 / 25.4 * 1000 );
231 _outputImage.setDotsPerMeterY( 96 / 25.4 * 1000 );
232 QPainter _p( &_outputImage );
233 QgsLayoutExporter _exporter( mLayout );
234 _exporter.renderPage( &_p, page );
235 _p.end();
236
237 if ( ! QDir( controlImagePath() ).exists() )
238 {
239 QDir().mkdir( controlImagePath() );
240 }
241 _outputImage.save( controlImagePath() + QDir::separator() + "expected_" + mTestName + ".png", "PNG" );
242 qDebug( ) << "Reference image saved to : " + controlImagePath() + QDir::separator() + "expected_" + mTestName + ".png";
243
244 }
245
246 QImage outputImage( mSize, QImage::Format_RGB32 );
247 outputImage.setDotsPerMeterX( mDotsPerMeter );
248 outputImage.setDotsPerMeterY( mDotsPerMeter );
249 drawBackground( &outputImage );
250 QPainter p( &outputImage );
251 QgsLayoutExporter exporter( mLayout );
252 exporter.renderPage( &p, page );
253 p.end();
254
255 QString renderedFilePath = QDir::tempPath() + '/' + QFileInfo( mTestName ).baseName() + "_rendered.png";
256 if ( QFile::exists( renderedFilePath ) )
257 QFile::remove( renderedFilePath );
258
259 outputImage.save( renderedFilePath, "PNG" );
260
261 setRenderedImage( renderedFilePath );
262
263 bool testResult = runTest( mTestName, pixelDiff );
264
265 checkedReport += report();
266
267 return testResult;
268#endif // QT_NO_PRINTER
269}
270
271
272
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:50
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.
QString report() const
Returns a HTML report for this test.
QgsMultiRenderChecker()
Constructor for QgsMultiRenderChecker.
QString markdownReport() const
Returns a markdown report for this test.
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).
static QDir testReportDir()
Returns the directory to use for generating a test report.
QString markdownReport(bool ignoreSuccess=true) const
Returns the markdown report describing the results of the test run.
void setMapSettings(const QgsMapSettings &mapSettings)
void setControlPathSuffix(const QString &name)
bool runTest(const QString &testName, unsigned int mismatchCount=0, QgsRenderChecker::Flags flags=QgsRenderChecker::Flags())
Render checker flags.
@ AvoidExportingRenderedImage
Avoids exporting rendered images to reports.
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.
QString report(bool ignoreSuccess=true) const
Returns the HTML report describing the results of the test run.
bool compareImages(const QString &testName, unsigned int mismatchCount=0, const QString &renderedImageFile=QString(), QgsRenderChecker::Flags flags=QgsRenderChecker::Flags())
Test using two arbitrary images (map renderer will not be used)
void setRenderedImage(const QString &imageFileName)
Sets the file name of the rendered image generated by the test.
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 setExpectFail(bool expectFail)
Sets whether the comparison is expected to fail.
void setColorTolerance(unsigned int colorTolerance)
Set tolerance for color components used by runTest() and compareImages().