1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QtTest/QtTest>
43 #include <paintcommands.h>
44 #include <QPainter>
45 #include <QLibraryInfo>
46 #include <baselineprotocol.h>
47 #include <QHash>
48
49 #ifndef QT_NO_OPENGL
50 #include <QtOpenGL>
51 #endif
52
53 #ifndef SRCDIR
54 #define SRCDIR "."
55 #endif
56
57 class tst_Lancelot : public QObject
58 {
59 Q_OBJECT
60
61 public:
62 tst_Lancelot();
63
64 static bool simfail;
65 static PlatformInfo clientInfo;
66
67 private:
68 enum GraphicsEngine {
69 Raster = 0,
70 OpenGL = 1
71 };
72
73 bool setupTestSuite(const QStringList& blacklist);
74 void runTestSuite(GraphicsEngine engine, QImage::Format format);
75 ImageItem render(const ImageItem &item, GraphicsEngine engine, QImage::Format format);
76 void paint(QPaintDevice *device, const QStringList &script, const QString &filePath);
77
78 BaselineProtocol proto;
79 ImageItemList baseList;
80 QHash<QString, QStringList> scripts;
81 bool dryRunMode;
82 QString scriptsDir;
83
84 private slots:
85 void initTestCase();
cleanupTestCase()86 void cleanupTestCase() {}
87
88 void testRasterARGB32PM_data();
89 void testRasterARGB32PM();
90 void testRasterRGB32_data();
91 void testRasterRGB32();
92 void testRasterRGB16_data();
93 void testRasterRGB16();
94
95 #ifndef QT_NO_OPENGL
96 void testOpenGL_data();
97 void testOpenGL();
98 #endif
99 };
100
101 bool tst_Lancelot::simfail = false;
102 PlatformInfo tst_Lancelot::clientInfo;
103
tst_Lancelot()104 tst_Lancelot::tst_Lancelot()
105 {
106 }
107
initTestCase()108 void tst_Lancelot::initTestCase()
109 {
110 // Check and setup the environment. We treat failures because of test environment
111 // (e.g. script files not found) as just warnings, and not QFAILs, to avoid false negatives
112 // caused by environment or server instability
113
114 #if defined(Q_OS_SOMEPLATFORM)
115 QSKIP("This test is not supported on this platform.", SkipAll);
116 #endif
117 if (!proto.connect(QLatin1String("tst_Lancelot"), &dryRunMode, clientInfo))
118 QSKIP(qPrintable(proto.errorMessage()), SkipAll);
119
120 #if defined(USE_RUNTIME_DIR)
121 scriptsDir = QCoreApplication::applicationDirPath() + "/scripts/";
122 #else
123 scriptsDir = SRCDIR "/scripts/";
124 #endif
125 QDir qpsDir(scriptsDir);
126 QStringList files = qpsDir.entryList(QStringList() << QLatin1String("*.qps"), QDir::Files | QDir::Readable);
127 if (files.isEmpty()) {
128 QWARN("No qps script files found in " + qpsDir.path().toLatin1());
129 QSKIP("Aborted due to errors.", SkipAll);
130 }
131
132 baseList.resize(files.count());
133 ImageItemList::iterator it = baseList.begin();
134 foreach(const QString& fileName, files) {
135 QFile file(scriptsDir + fileName);
136 file.open(QFile::ReadOnly);
137 QByteArray cont = file.readAll();
138 scripts.insert(fileName, QString::fromLatin1(cont).split(QLatin1Char('\n'), QString::SkipEmptyParts));
139 it->itemName = fileName;
140 it->itemChecksum = qChecksum(cont.constData(), cont.size());
141 it++;
142 }
143 }
144
145
testRasterARGB32PM_data()146 void tst_Lancelot::testRasterARGB32PM_data()
147 {
148 QStringList localBlacklist;
149 if (!setupTestSuite(localBlacklist))
150 QSKIP("Communication with baseline image server failed.", SkipAll);
151 }
152
153
testRasterARGB32PM()154 void tst_Lancelot::testRasterARGB32PM()
155 {
156 runTestSuite(Raster, QImage::Format_ARGB32_Premultiplied);
157 }
158
159
testRasterRGB32_data()160 void tst_Lancelot::testRasterRGB32_data()
161 {
162 QStringList localBlacklist;
163 if (!setupTestSuite(localBlacklist))
164 QSKIP("Communication with baseline image server failed.", SkipAll);
165 }
166
167
testRasterRGB32()168 void tst_Lancelot::testRasterRGB32()
169 {
170 runTestSuite(Raster, QImage::Format_RGB32);
171 }
172
173
testRasterRGB16_data()174 void tst_Lancelot::testRasterRGB16_data()
175 {
176 QStringList localBlacklist;
177 if (!setupTestSuite(localBlacklist))
178 QSKIP("Communication with baseline image server failed.", SkipAll);
179 }
180
181
testRasterRGB16()182 void tst_Lancelot::testRasterRGB16()
183 {
184 runTestSuite(Raster, QImage::Format_RGB16);
185 }
186
187
188 #ifndef QT_NO_OPENGL
testOpenGL_data()189 void tst_Lancelot::testOpenGL_data()
190 {
191 QStringList localBlacklist = QStringList() << QLatin1String("rasterops.qps");
192 if (!setupTestSuite(localBlacklist))
193 QSKIP("Communication with baseline image server failed.", SkipAll);
194 }
195
196
testOpenGL()197 void tst_Lancelot::testOpenGL()
198 {
199 bool ok = false;
200 QGLWidget glWidget;
201 if (glWidget.isValid() && glWidget.format().directRendering()
202 && ((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0)
203 || (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0))
204 && QGLFramebufferObject::hasOpenGLFramebufferObjects())
205 {
206 glWidget.makeCurrent();
207 if (!QByteArray((const char *)glGetString(GL_VERSION)).contains("Mesa"))
208 ok = true;
209 }
210 if (ok)
211 runTestSuite(OpenGL, QImage::Format_RGB32);
212 else
213 QSKIP("System under test does not meet preconditions for GL testing. Skipping.", SkipAll);
214 }
215 #endif
216
217
setupTestSuite(const QStringList & blacklist)218 bool tst_Lancelot::setupTestSuite(const QStringList& blacklist)
219 {
220 QTest::addColumn<ImageItem>("baseline");
221
222 ImageItemList itemList(baseList);
223 if (!proto.requestBaselineChecksums(QTest::currentTestFunction(), &itemList)) {
224 QWARN(qPrintable(proto.errorMessage()));
225 return false;
226 }
227
228 foreach(const ImageItem& item, itemList) {
229 if (!blacklist.contains(item.itemName))
230 QTest::newRow(item.itemName.toLatin1()) << item;
231 }
232 return true;
233 }
234
235
runTestSuite(GraphicsEngine engine,QImage::Format format)236 void tst_Lancelot::runTestSuite(GraphicsEngine engine, QImage::Format format)
237 {
238 QFETCH(ImageItem, baseline);
239
240 if (baseline.status == ImageItem::IgnoreItem)
241 QSKIP("Blacklisted by baseline server.", SkipSingle);
242
243 ImageItem rendered = render(baseline, engine, format);
244 static int consecutiveErrs = 0;
245 if (rendered.image.isNull()) { // Assume an error in the test environment, not Qt
246 QWARN("Error: Failed to render image.");
247 if (++consecutiveErrs < 3) {
248 QSKIP("Aborted due to errors.", SkipSingle);
249 } else {
250 consecutiveErrs = 0;
251 QSKIP("Too many errors, skipping rest of testfunction.", SkipAll);
252 }
253 } else {
254 consecutiveErrs = 0;
255 }
256
257
258 if (baseline.status == ImageItem::BaselineNotFound) {
259 if (!proto.submitNewBaseline(rendered, 0))
260 QWARN("Failed to submit new baseline: " + proto.errorMessage().toLatin1());
261 QSKIP("Baseline not found; new baseline created.", SkipSingle);
262 }
263
264 if (!baseline.imageChecksums.contains(rendered.imageChecksums.at(0))) {
265 QByteArray serverMsg;
266 if (!proto.submitMismatch(rendered, &serverMsg))
267 serverMsg = "Failed to submit mismatching image to server.";
268 if (dryRunMode)
269 qDebug() << "Dryrun mode, ignoring detected mismatch." << serverMsg;
270 else
271 QFAIL("Rendered image differs from baseline. Report:\n " + serverMsg);
272 }
273 }
274
275
render(const ImageItem & item,GraphicsEngine engine,QImage::Format format)276 ImageItem tst_Lancelot::render(const ImageItem &item, GraphicsEngine engine, QImage::Format format)
277 {
278 ImageItem res = item;
279 res.imageChecksums.clear();
280 res.image = QImage();
281 QString filePath = scriptsDir + item.itemName;
282 QStringList script = scripts.value(item.itemName);
283
284 if (engine == Raster) {
285 QImage img(800, 800, format);
286 paint(&img, script, QFileInfo(filePath).absoluteFilePath()); // eh yuck (filePath stuff)
287 res.image = img;
288 res.imageChecksums.append(ImageItem::computeChecksum(img));
289 #ifndef QT_NO_OPENGL
290 } else if (engine == OpenGL) {
291 QGLWidget glWidget;
292 if (glWidget.isValid()) {
293 glWidget.makeCurrent();
294 QGLFramebufferObjectFormat fboFormat;
295 fboFormat.setSamples(16);
296 fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
297 QGLFramebufferObject fbo(800, 800, fboFormat);
298 paint(&fbo, script, QFileInfo(filePath).absoluteFilePath()); // eh yuck (filePath stuff)
299 res.image = fbo.toImage().convertToFormat(format);
300 res.imageChecksums.append(ImageItem::computeChecksum(res.image));
301 }
302 #endif
303 }
304
305 return res;
306 }
307
paint(QPaintDevice * device,const QStringList & script,const QString & filePath)308 void tst_Lancelot::paint(QPaintDevice *device, const QStringList &script, const QString &filePath)
309 {
310 QPainter p(device);
311 PaintCommands pcmd(script, 800, 800);
312 //pcmd.setShouldDrawText(false);
313 pcmd.setType(ImageType);
314 pcmd.setPainter(&p);
315 pcmd.setFilePath(filePath);
316 pcmd.runCommands();
317 p.end();
318
319 if (simfail) {
320 QPainter p2(device);
321 p2.setPen(QPen(QBrush(Qt::cyan), 3, Qt::DashLine));
322 p2.drawLine(200, 200, 600, 600);
323 p2.drawLine(600, 200, 200, 600);
324 simfail = false;
325 }
326 }
327
328 #define main rmain
QTEST_MAIN(tst_Lancelot)329 QTEST_MAIN(tst_Lancelot)
330 #undef main
331
332 int main(int argc, char *argv[])
333 {
334 tst_Lancelot::clientInfo = PlatformInfo::localHostInfo();
335
336 char *fargv[20];
337 int fargc = 0;
338 for (int i = 0; i < qMin(argc, 19); i++) {
339 if (!qstrcmp(argv[i], "-simfail")) {
340 tst_Lancelot::simfail = true;
341 } else if (!qstrcmp(argv[i], "-compareto") && i < argc-1) {
342 QString arg = QString::fromLocal8Bit(argv[++i]);
343 int split = arg.indexOf(QLC('='));
344 if (split < 0)
345 continue;
346 QString key = arg.left(split).trimmed();
347 QString value = arg.mid(split+1).trimmed();
348 if (key.isEmpty() || value.isEmpty())
349 continue;
350 tst_Lancelot::clientInfo.addOverride(key, value);
351 } else {
352 fargv[fargc++] = argv[i];
353 }
354 }
355 fargv[fargc] = 0;
356 return rmain(fargc, fargv);
357 }
358
359 #include "tst_lancelot.moc"
360