1
/****************************************************************************
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/legal
6
** This file is part of the test suite of the Qt Toolkit.
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 Digia. For licensing terms and
14
** conditions see http://qt.digia.com/licensing. For further information
15
** use the contact form at http://qt.digia.com/contact-us.
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 as published by the Free Software
20
** Foundation and appearing in the file LICENSE.LGPL included in the
21
** packaging of this file. Please review the following information to
22
** ensure the GNU Lesser General Public License version 2.1 requirements
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25
** In addition, as a special exception, Digia gives you certain additional
26
** rights. These rights are described in the Digia Qt LGPL Exception
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29
** GNU General Public License Usage
30
** Alternatively, this file may be used under the terms of the GNU
31
** General Public License version 3.0 as published by the Free Software
32
** Foundation and appearing in the file LICENSE.GPL included in the
33
** packaging of this file. Please review the following information to
34
** ensure the GNU General Public License version 3.0 requirements will be
35
** met: http://www.gnu.org/copyleft/gpl.html.
40
****************************************************************************/
43
#include <atWrapper.h>
44
#include <datagenerator/datagenerator.h>
54
#include <QtTest/QSignalSpy>
55
#include <QLibraryInfo>
57
static const char *ArthurDir = "../../arthur";
61
atWrapper::atWrapper()
68
bool atWrapper::initTests(bool *haveBaseline)
70
qDebug() << "Running test on buildkey:" << QLibraryInfo::buildKey() << " qt version:" << qVersion();
72
qDebug( "Initializing tests..." );
74
if (!loadConfig( QHostInfo::localHostName().split( "." ).first() + ".ini" ))
77
//Reset the FTP environment where the results are stored
78
*haveBaseline = setupFTP();
80
// Retrieve the latest test result baseline from the FTP server.
85
void atWrapper::downloadBaseline()
88
qDebug() << "Now downloading baseline...";
92
QObject::connect( &ftp, SIGNAL(listInfo(QUrlInfo)), this, SLOT(ftpMgetAddToList(QUrlInfo)) );
94
//Making sure that the needed local directories exist.
96
QHashIterator<QString, QString> j(enginesToTest);
104
if ( !dir.cd( j.key() + ".baseline" ) )
105
dir.mkdir( j.key() + ".baseline" );
109
//FTP to the host specified in the config file, and retrieve the test result baseline.
110
ftp.connectToHost( ftpHost );
111
ftp.login( ftpUser, ftpPass );
113
ftp.cd( ftpBaseDir );
115
QHashIterator<QString, QString> i(enginesToTest);
116
while ( i.hasNext() )
120
mgetDirList << i.key() + ".baseline";
121
ftp.cd( i.key() + ".baseline" );
125
while ( ftp.hasPendingCommands() )
126
QCoreApplication::instance()->processEvents();
134
while ( ftp.hasPendingCommands() )
135
QCoreApplication::instance()->processEvents();
139
void atWrapper::ftpMgetAddToList( const QUrlInfo &urlInfo )
141
//Simply adding to the list of files to download.
142
mgetDirList << urlInfo.name();
146
void atWrapper::ftpMgetDone( bool error)
150
//Downloading the files listed in mgetDirList...
152
ftp.connectToHost( ftpHost );
153
ftp.login( ftpUser, ftpPass );
157
if ( mgetDirList.size() > 1 )
158
for ( int i = 1; i < mgetDirList.size(); ++i )
160
file = new QFile( QString( output ) + "/" + mgetDirList.at( 0 ) + "/" + mgetDirList.at( i ) );
161
if (file->open(QIODevice::WriteOnly)) {
162
ftp.get( ftpBaseDir + "/" + mgetDirList.at( 0 ) + "/" + mgetDirList.at( i ), file );
163
ftp.list(); //Only there to fill up a slot in the pendingCommands queue.
164
while ( ftp.hasPendingCommands() )
165
QCoreApplication::instance()->processEvents();
168
qDebug() << "Couldn't open file for writing: " << file->fileName();
173
while ( ftp.hasPendingCommands() )
174
QCoreApplication::instance()->processEvents();
177
void atWrapper::uploadFailed( QString dir, QString filename, QByteArray filedata )
179
//Upload a failed test case image to the FTP server.
181
ftp.connectToHost( ftpHost );
182
ftp.login( ftpUser, ftpPass );
184
ftp.cd( ftpBaseDir );
187
ftp.put( filedata, filename, QFtp::Binary );
191
while ( ftp.hasPendingCommands() )
192
QCoreApplication::instance()->processEvents();
195
// returns false if no baseline exists
196
bool atWrapper::setupFTP()
198
qDebug( "Setting up FTP environment" );
201
ftpMkDir( ftpBaseDir );
203
ftpBaseDir += "/" + QLibraryInfo::buildKey();
205
ftpMkDir( ftpBaseDir );
207
ftpBaseDir += "/" + QString( qVersion() );
209
ftpMkDir( ftpBaseDir );
211
QHashIterator<QString, QString> i(enginesToTest);
212
QHashIterator<QString, QString> j(enginesToTest);
214
bool haveBaseline = true;
215
//Creating the baseline directories for each engine
216
while ( i.hasNext() )
219
//qDebug() << "Creating dir with key:" << i.key();
220
ftpMkDir( ftpBaseDir + "/" + QString( i.key() ) + ".failed" );
221
ftpMkDir( ftpBaseDir + "/" + QString( i.key() ) + ".diff" );
222
if (!ftpMkDir( ftpBaseDir + "/" + QString( i.key() ) + ".baseline" ))
223
haveBaseline = false;
228
ftp.connectToHost( ftpHost );
229
ftp.login( ftpUser, ftpPass );
231
ftp.cd( ftpBaseDir );
232
//Deleting previous failed directory and all the files in it, then recreating it.
233
while ( j.hasNext() )
237
rmDirList << ftpBaseDir + "/" + j.key() + ".failed" + "/";
238
ftpRmDir( j.key() + ".failed" );
239
ftp.rmdir( j.key() + ".failed" );
240
ftp.mkdir( j.key() + ".failed" );
243
while ( ftp.hasPendingCommands() )
244
QCoreApplication::instance()->processEvents();
247
rmDirList << ftpBaseDir + "/" + j.key() + ".diff" + "/";
248
ftpRmDir( j.key() + ".diff" );
249
ftp.rmdir( j.key() + ".diff" );
250
ftp.mkdir( j.key() + ".diff" );
253
while ( ftp.hasPendingCommands() )
254
QCoreApplication::instance()->processEvents();
260
while ( ftp.hasPendingCommands() )
261
QCoreApplication::instance()->processEvents();
266
void atWrapper::ftpRmDir( QString dir )
268
//Hack to remove a populated directory. (caveat: containing only files and empty dirs, not recursive!)
269
qDebug() << "Now removing directory: " << dir;
271
QObject::connect( &ftp, SIGNAL(listInfo(QUrlInfo)), this, SLOT(ftpRmDirAddToList(QUrlInfo)) );
272
QObject::connect( &ftp, SIGNAL(done(bool)), this, SLOT(ftpRmDirDone(bool)) );
274
ftp.connectToHost( ftpHost );
275
ftp.login( ftpUser, ftpPass );
277
ftp.list( ftpBaseDir + "/" + dir );
281
while ( ftp.hasPendingCommands() )
282
QCoreApplication::instance()->processEvents();
285
void atWrapper::ftpRmDirDone( bool error )
287
//Deleting each file in the directory listning, rmDirList.
291
ftp.connectToHost( ftpHost );
292
ftp.login( ftpUser, ftpPass );
294
if ( rmDirList.size() > 1 )
295
for (int i = 1; i < rmDirList.size(); ++i)
296
ftp.remove( rmDirList.at(0) + rmDirList.at( i ) );
300
while ( ftp.hasPendingCommands() )
301
QCoreApplication::instance()->processEvents();
304
// returns false if the directory already exists
305
bool atWrapper::ftpMkDir( QString dir )
307
//Simply used to avoid QFTP from bailing out and loosing a queue of commands.
311
QSignalSpy commandSpy(&ftp, SIGNAL(commandFinished(int,bool)));
313
ftp.connectToHost( ftpHost );
314
ftp.login( ftpUser, ftpPass );
315
const int command = ftp.mkdir( dir );
318
while ( ftp.hasPendingCommands() )
319
QCoreApplication::instance()->processEvents();
321
for (int i = 0; i < commandSpy.count(); ++i)
322
if (commandSpy.at(i).at(0) == command)
323
return commandSpy.at(i).at(1).toBool();
329
void atWrapper::ftpRmDirAddToList( const QUrlInfo &urlInfo )
331
//Just adding the file to the list for deletion
332
rmDirList << urlInfo.name();
336
bool atWrapper::executeTests()
338
qDebug("Executing the tests...");
340
QHashIterator<QString, QString> i(enginesToTest);
342
DataGenerator generator;
344
//Running datagenerator against all the frameworks specified in the config file.
345
while ( i.hasNext() )
350
qDebug( "Now testing: " + i.key().toLatin1() );
353
//./bin/datagenerator -framework data/framework.ini -engine OpenGL -suite 1.1 -output outtest
356
QByteArray eng = i.key().toLatin1();
357
QByteArray fwk = framework.toLatin1();
358
QByteArray sut = suite.toLatin1();
359
QByteArray out = output.toLatin1();
360
QByteArray siz = size.toLatin1();
361
QByteArray fill = fillColor.toLatin1();
363
params[1] = "-framework";
364
params[2] = fwk.data();
365
params[3] = "-engine";
366
params[4] = eng.data();
367
params[5] = "-suite";
368
params[6] = sut.data();
369
params[7] = "-output";
370
params[8] = out.data();
372
params[10] = siz.data();
373
params[11] = "-fill";
374
params[12] = fill.data();
376
generator.run( 13, params );
382
void atWrapper::createBaseline()
384
qDebug( "Now uploading a baseline of only the latest test values" );
386
QHashIterator<QString, QString> i(enginesToTest);
390
ftp.connectToHost( ftpHost );
391
ftp.login( ftpUser, ftpPass );
392
ftp.cd( ftpBaseDir );
393
//Upload all the latest test results to the FTP server's baseline directory.
394
while ( i.hasNext() )
399
ftp.cd( i.key() + ".baseline" );
400
dir.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks);
401
dir.setNameFilters( QStringList() << "*.png" );
402
QFileInfoList list = dir.entryInfoList();
404
for (int n = 0; n < list.size(); n++)
406
QFileInfo fileInfo = list.at( n );
407
QFile file( QString( output ) + "/" + i.key() + "/" + fileInfo.fileName() );
408
file.open( QIODevice::ReadOnly );
409
QByteArray fileData = file.readAll();
410
//qDebug() << "Sending up:" << fileInfo.fileName() << "with file size" << fileData.size();
412
ftp.put( fileData, fileInfo.fileName(), QFtp::Binary );
420
while ( ftp.hasPendingCommands() )
421
QCoreApplication::instance()->processEvents();
424
bool atWrapper::compare()
426
qDebug( "Now comparing the results to the baseline" );
428
QHashIterator<QString, QString> i(enginesToTest);
430
while ( i.hasNext() )
434
compareDirs( output , i.key() );
441
void atWrapper::compareDirs( QString basedir, QString target )
446
/* The following should be redundant now.
448
if ( !dir.cd( target + ".failed" ) )
449
dir.mkdir( target + ".failed" );
455
if ( !dir.cd( target + ".diff" ) )
456
dir.mkdir( target + ".diff" );
462
//Perform comparisons between the two directories.
464
dir.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks);
465
dir.setNameFilters( QStringList() << "*.png" );
466
dir.cd( target + ".baseline" );
467
QFileInfoList list = dir.entryInfoList();
469
for (int i = 0; i < list.size(); ++i)
471
QFileInfo fileInfo = list.at(i);
472
diff ( basedir, target, fileInfo.fileName() );
476
bool atWrapper::diff( QString basedir, QString dir, QString target )
478
//Comparing the two specified files, and then uploading them to
479
//the ftp server if they differ
481
basedir += "/" + dir;
482
QString one = basedir + ".baseline/" + target;
483
QString two = basedir + "/" + target;
487
file.open( QIODevice::ReadOnly );
488
QByteArray contentsOfOne = file.readAll();
491
file.setFileName( two );
493
file.open( QIODevice::ReadOnly );
494
QByteArray contentsOfTwo = file.readAll();
497
if ( contentsOfTwo.size() == 0 )
499
qDebug() << "No test result found for baseline: " << one;
500
file.setFileName( one );
501
file.open( QIODevice::ReadOnly );
502
file.copy( basedir + ".failed/" + target + "_missing" );
503
uploadFailed( dir + ".failed", target + "_missing", contentsOfTwo );
508
if ( ( memcmp( contentsOfOne, contentsOfTwo, contentsOfOne.size() ) ) == 0 )
512
qDebug() << "Sorry, the result did not match: " << one;
513
file.setFileName( two );
514
file.open( QIODevice::ReadOnly );
515
file.copy( basedir + ".failed/" + target );
517
uploadFailed( dir + ".failed", target, contentsOfTwo );
518
uploadDiff( basedir, dir, target );
523
void atWrapper::uploadDiff( QString basedir, QString dir, QString filename )
527
QImage im1( basedir + ".baseline/" + filename );
528
QImage im2( basedir + "/" + filename );
530
QImage im3(im1.size(), QImage::Format_ARGB32);
532
im1 = im1.convertToFormat(QImage::Format_ARGB32);
533
im2 = im2.convertToFormat(QImage::Format_ARGB32);
535
for ( int y=0; y<im1.height(); ++y )
537
uint *s = (uint *) im1.scanLine(y);
538
uint *d = (uint *) im2.scanLine(y);
539
uint *w = (uint *) im3.scanLine(y);
541
for ( int x=0; x<im1.width(); ++x )
553
im3.save( basedir + ".diff/" + filename ,"PNG");
555
QFile file( basedir + ".diff/" + filename );
556
file.open( QIODevice::ReadOnly );
557
QByteArray contents = file.readAll();
560
uploadFailed( dir + ".diff", filename, contents );
564
bool atWrapper::loadConfig( QString path )
566
qDebug() << "Loading config file from ... " << path;
568
//If there is no config file, don't proceed;
569
if ( !QFile::exists( path ) )
575
QSettings settings( path, QSettings::IniFormat, this );
578
//FIXME: Switch to QStringList or something, hash is not needed!
579
int numEngines = settings.beginReadArray("engines");
581
for ( int i = 0; i < numEngines; ++i )
583
settings.setArrayIndex(i);
584
enginesToTest.insert( settings.value( "engine" ).toString(), "Info here please :p" );
589
framework = QString(ArthurDir) + QDir::separator() + settings.value( "framework" ).toString();
590
suite = settings.value( "suite" ).toString();
591
output = settings.value( "output" ).toString();
592
size = settings.value( "size", "480,360" ).toString();
593
fillColor = settings.value( "fill", "white" ).toString();
594
ftpUser = settings.value( "ftpUser" ).toString();
595
ftpPass = settings.value( "ftpPass" ).toString();
596
ftpHost = settings.value( "ftpHost" ).toString();
597
ftpBaseDir = settings.value( "ftpBaseDir" ).toString();
600
QDir::current().mkdir( output );
602
output += "/" + QLibraryInfo::buildKey();
604
QDir::current().mkdir( output );
606
output += "/" + QString( qVersion() );
608
QDir::current().mkdir( output );
611
ftpBaseDir += "/" + QHostInfo::localHostName().split( "." ).first();
615
framework = "data/framework.ini";
617
output = "testresults";
618
ftpUser = "anonymous";
619
ftpPass = "anonymouspass";
620
ftpHost = "kramer.troll.no";
621
ftpBaseDir = "/arthurtest";
626
bool atWrapper::runAutoTests()
628
//SVG needs this widget...
631
bool haveBaseline = false;
633
if (!initTests(&haveBaseline))
639
qDebug( " First run! Creating baseline..." );
644
qDebug( " Comparing results..." );