QGIS API Documentation 3.27.0-Master (f261cc1f8b)
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 <QDebug>
19
21{
22 if ( qgetenv( "QGIS_CONTINUOUS_INTEGRATION_RUN" ) == QStringLiteral( "true" ) )
23 mIsCiRun = true;
24}
25
26void QgsMultiRenderChecker::setControlName( const QString &name )
27{
28 mControlName = name;
29}
30
32{
33 mControlPathPrefix = prefix;
34}
35
37{
38 mMapSettings = mapSettings;
39}
40
41bool QgsMultiRenderChecker::runTest( const QString &testName, unsigned int mismatchCount )
42{
43 mResult = false;
44
45 mReport += "<h2>" + testName + "</h2>\n";
46
47 const QString baseDir = controlImagePath();
48
49 QStringList subDirs = QDir( baseDir ).entryList( QDir::Dirs | QDir::NoDotAndDotDot );
50
51 if ( subDirs.isEmpty() )
52 {
53 subDirs << QString();
54 }
55
56 QVector<QgsDartMeasurement> dartMeasurements;
57
58 for ( const QString &suffix : std::as_const( subDirs ) )
59 {
60 if ( subDirs.count() > 1 )
61 {
62 qDebug() << "Checking subdir " << suffix;
63 }
64 bool result;
65 QgsRenderChecker checker;
66 checker.enableDashBuffering( true );
67 checker.setColorTolerance( mColorTolerance );
68 checker.setSizeTolerance( mMaxSizeDifferenceX, mMaxSizeDifferenceY );
69 checker.setControlPathPrefix( mControlPathPrefix );
70 checker.setControlPathSuffix( suffix );
71 checker.setControlName( mControlName );
72 checker.setMapSettings( mMapSettings );
73
74 if ( !mRenderedImage.isNull() )
75 {
76 checker.setRenderedImage( mRenderedImage );
77 result = checker.compareImages( testName, mismatchCount, mRenderedImage );
78 }
79 else
80 {
81 result = checker.runTest( testName, mismatchCount );
82 mRenderedImage = checker.renderedImage();
83 }
84
85 mResult |= result;
86
87 dartMeasurements << checker.dartMeasurements();
88
89 mReport += checker.report( false );
90 }
91
92 if ( !mResult && mIsCiRun )
93 {
94 const auto constDartMeasurements = dartMeasurements;
95 for ( const QgsDartMeasurement &measurement : constDartMeasurements )
96 measurement.send();
97
98 QgsDartMeasurement msg( QStringLiteral( "Image not accepted by test" ), QgsDartMeasurement::Text, "This may be caused because the test is supposed to fail or rendering inconsistencies."
99 "If this is a rendering inconsistency, please add another control image folder, add an anomaly image or increase the color tolerance." );
100 msg.send();
101 }
102
103 return mResult;
104}
105
107{
108 return !mResult ? mReport : QString();
109}
110
112{
113 QString myDataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
114 QString myControlImageDir = myDataDir + QDir::separator() + "control_images" +
115 QDir::separator() + mControlPathPrefix + QDir::separator() + mControlName + QDir::separator();
116 return myControlImageDir;
117}
118
119//
120// QgsLayoutChecker
121//
122
124
125QgsLayoutChecker::QgsLayoutChecker( const QString &testName, QgsLayout *layout )
126 : mTestName( testName )
127 , mLayout( layout )
128 , mSize( 1122, 794 )
129 , mDotsPerMeter( 96 / 25.4 * 1000 )
130{
131 // Qt has some slight render inconsistencies on the whole image sometimes
133}
134
135bool QgsLayoutChecker::testLayout( QString &checkedReport, int page, int pixelDiff, bool createReferenceImage )
136{
137#ifdef QT_NO_PRINTER
138 return false;
139#else
140 if ( !mLayout )
141 {
142 return false;
143 }
144
145 setControlName( "expected_" + mTestName );
146
147
148 if ( createReferenceImage )
149 {
150 //fake mode to generate expected image
151 //assume 96 dpi
152
153
154 QImage _outputImage( mSize, QImage::Format_RGB32 );
155 _outputImage.setDotsPerMeterX( 96 / 25.4 * 1000 );
156 _outputImage.setDotsPerMeterY( 96 / 25.4 * 1000 );
157 QPainter _p( &_outputImage );
158 QgsLayoutExporter _exporter( mLayout );
159 _exporter.renderPage( &_p, page );
160 _p.end();
161
162 if ( ! QDir( controlImagePath() ).exists() )
163 {
164 QDir().mkdir( controlImagePath() );
165 }
166 _outputImage.save( controlImagePath() + QDir::separator() + "expected_" + mTestName + ".png", "PNG" );
167 qDebug( ) << "Reference image saved to : " + controlImagePath() + QDir::separator() + "expected_" + mTestName + ".png";
168
169 }
170
171 QImage outputImage( mSize, QImage::Format_RGB32 );
172 outputImage.setDotsPerMeterX( mDotsPerMeter );
173 outputImage.setDotsPerMeterY( mDotsPerMeter );
174 drawBackground( &outputImage );
175 QPainter p( &outputImage );
176 QgsLayoutExporter exporter( mLayout );
177 exporter.renderPage( &p, page );
178 p.end();
179
180 QString renderedFilePath = QDir::tempPath() + '/' + QFileInfo( mTestName ).baseName() + "_rendered.png";
181 outputImage.save( renderedFilePath, "PNG" );
182
183 setRenderedImage( renderedFilePath );
184
185 bool testResult = runTest( mTestName, pixelDiff );
186
187 checkedReport += report();
188
189 return testResult;
190#endif // QT_NO_PRINTER
191}
192
193
194
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.
QString report() const
Returns a report for this test.
QgsMultiRenderChecker()
Constructor for QgsMultiRenderChecker.
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.
QString report(bool ignoreSuccess=true) const
Returns the HTML report describing the results of the test run.
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().
bool compareImages(const QString &testName, unsigned int mismatchCount=0, const QString &renderedImageFile=QString())
Test using two arbitrary images (map renderer will not be used)