# include <commands.hxx>
# include <webpage.hxx>
# include <QApplication>
# include <QFile>
# include <QWebView>
# include <QStringList>
# include <QCommandLineParser>
# include <iostream>
# include <sstream>
# include <fstream>
# include <QDateTime>
# include <xml-cxx/xml.hxx>
# include <mrw/string.hxx>
# include <version.hxx>
using namespace NAMESPACE ;
/** @page junitXml Test Output XML Format
The test output XML format emulates JUnit ' s format , so that JUnit
tools can be used to evaluate ist , namely on tools like jenkins .
@ code { . xml }
< testsuites >
< testsuite name = " ... " timestamp = " ... " failures = " ... " >
< testcase classname = " ... " name = " ... " >
< failure message = " ... " / >
< system - out > . . . < / system - out >
< system - err > . . . < / system - err >
< / testcase >
< testcase . . . / >
< / testsuite >
< testsuite . . . >
. . .
< / testsuite >
< testsuites >
@ endcode
The default name for a testsuite is the name of the test script
file . it can be overwritten using the @ c testsuite statement . The
attribute @ c failures contains the number of failed test cases .
The default classname for a testcase is @ c testsuite - preparation
before the script runs , then in the script it is the name of the
current command and the classname equals to the testsuite ' s name .
If you specify testsuites , testcases in your script file , then
this overwrites the defaults . Here @ c testsuite maps to the
testsuite ' s name , @ c testcase maps to the next testcase ' s
classname .
*/
QString format ( QString txt , int indent = 2 , int cpl = 80 ) {
QStringList res ;
QStringList lines ( txt . split ( ' \n ' ) ) ;
QString ind ( indent , ' ' ) ;
Q_FOREACH ( QString line , lines ) {
line . insert ( 0 , ind ) ;
for ( int pos ( indent ) ; line . size ( ) - pos > cpl ; + + pos ) {
int pos2 = line . lastIndexOf ( ' ' , pos + cpl ) ;
if ( pos2 > pos ) pos = pos2 ;
else pos = line . indexOf ( ' ' , pos + 1 ) ;
if ( pos < 0 ) break ;
line . remove ( pos , 1 ) ;
line . insert ( pos , " \n " + ind ) ;
}
res + = line ;
}
return res . join ( ' \n ' ) ;
}
QString format ( std : : string txt , int indent = 2 , int cpl = 60 ) {
return format ( QString : : fromStdString ( txt ) , indent , cpl ) ;
}
QString help ( const Script & s ) {
std : : ostringstream ss ;
ss < < " Synopsis " < < std : : endl
< < std : : endl
< < " webrunner [<OPTIONS>] [<file1> [<file2> [...]]] " < < std : : endl
< < std : : endl
< < " DESCRIPTION " < < std : : endl
< < std : : endl
< < format ( description ( ) ) . toStdString ( ) < < std : : endl
< < std : : endl
< < format ( readme ( ) ) . toStdString ( ) < < std : : endl
< < std : : endl
< < " SCRIPT SYNTAX " < < std : : endl
< < std : : endl
< < format ( s . syntax ( ) ) . toStdString ( ) < < std : : endl
< < std : : endl
< < " SCRIPT COMMANDS " < < std : : endl
< < std : : endl
< < format ( s . commands ( ) ) . toStdString ( ) < < std : : endl ;
return QString : : fromStdString ( ss . str ( ) ) ;
}
int main ( int argc , char * argv [ ] ) try {
bool failed ( false ) ;
QApplication a ( argc , argv ) ;
QWebView p ;
p . setPage ( new TestWebPage ( & p , true ) ) ;
QCommandLineParser parser ;
Script script ;
parser . setApplicationDescription ( help ( script ) ) ;
parser . addHelpOption ( ) ;
parser . addOption ( QCommandLineOption
( QStringList ( ) < < " p " < < " path " ,
" search <path> within the test scripts " , " path " ) ) ;
parser . addOption ( QCommandLineOption
( QStringList ( ) < < " x " < < " xml " ,
" store XML output in <xmlfile> " , " xmlfile " ) ) ;
parser . addOption ( QCommandLineOption
( QStringList ( ) < < " r " < < " retries " ,
" on error retry up to <maxretries> times " ,
" maxretries " , " 0 " ) ) ;
parser . addOption ( QCommandLineOption
( QStringList ( ) < < " W " < < " width " ,
" set screenshot size to <width> pixel " , " width " , " 2048 " ) ) ;
parser . addOption ( QCommandLineOption
( QStringList ( ) < < " H " < < " height " ,
" set screenshot size to <height> pixel " , " height " , " 2048 " ) ) ;
parser . addOption ( QCommandLineOption
( QStringList ( ) < < " v " < < " version " ,
" show version information " ) ) ;
parser . addOption ( QCommandLineOption
( QStringList ( ) < < " s " < < " skipped " ,
" treat skipped test cases as failure in XML output file " ) ) ;
parser . addOption ( QCommandLineOption
( QStringList ( ) < < " t " < < " target-path " ,
" set screenshot target path to <path> " , " path " ,
QDir ( ) . absolutePath ( ) + QDir : : separator ( ) + " attachments " ) ) ;
parser . process ( a ) ;
if ( parser . isSet ( " version " ) ) {
std : : cout < < * argv < < std : : endl
< < " from package " < < package_string ( ) < < std : : endl
< < " by " < < author ( ) < < std : : endl
< < " built on " < < build_date ( ) < < std : : endl ;
return 0 ;
}
if ( parser . isSet ( " path " ) ) script . path ( parser . value ( " path " ) ) ;
int retries ( parser . value ( " retries " ) . toInt ( ) ) ;
int width ( parser . value ( " width " ) . toInt ( ) ) ;
int height ( parser . value ( " height " ) . toInt ( ) ) ;
QString target ( parser . value ( " target-path " ) ) ;
p . resize ( width , height ) ;
std : : shared_ptr < xml : : Node > testsuites ( new xml : : Node ( " testsuites " ) ) ;
Q_FOREACH ( QString file , parser . positionalArguments ( ) ) {
int expectedtestcases ( - 1 ) ;
xml : : Node testsuite ( " testsuite " ) ;
testsuite . attr ( " name " ) = QFileInfo ( file ) . baseName ( ) . toStdString ( ) ;
testsuite . attr ( " timestamp " ) =
QDateTime : : currentDateTime ( ) . toString ( Qt : : ISODate ) . toStdString ( ) ;
xml : : Node testcase ( " testcase " ) ;
testcase . attr ( " classname " ) = " testsuite-preparation " ;
try {
script . reset ( ) ;
script . log ( " ===================== " ) ;
script . log ( " TEST: " + file ) ;
script . log ( " --------------------- " ) ;
testcase . attr ( " name " ) = " open test suite file " ;
testsuite < < testcase ;
QFile f ( file ) ;
if ( ! f . open ( QIODevice : : ReadOnly ) )
throw std : : runtime_error ( " cannot open file " + file . toStdString ( ) ) ;
QString txt ( QString : : fromUtf8 ( f . readAll ( ) ) ) ;
testcase . attr ( " name " ) = " parse test suite file " ;
testsuite < < testcase ;
script . parse ( txt . split ( ' \n ' ) , file ) ;
expectedtestcases = script . steps ( ) + 2 ;
if ( failed ) {
script . log ( " FAILED: " + file + " skipped due to previous error " ) ;
testcase . attr ( " name " ) = " testsuite " ;
xml : : Node failure ( " failure " ) ;
failure . attr ( " message " ) = " ignored due to previous failure " ;
testsuite < < ( testcase < < failure ) ;
testsuite . attr ( " failures " ) =
mrw : : string ( expectedtestcases + 1 - testsuite . children ( ) ) ;
if ( parser . isSet ( " skipped " ) ) {
testcase . attr ( " name " ) = " skipped test case " ;
failure . attr ( " message " ) = " skipped due to previous failure " ;
testcase < < failure ;
for ( int i ( testsuite . children ( ) ) ; i < expectedtestcases ; + + i )
testsuite < < testcase ;
}
} else {
testsuite . attr ( " failures " ) = " 0 " ;
( * testsuites ) < < testsuite ;
script . run ( p . page ( ) - > mainFrame ( ) , testsuites ,
target + QDir : : separator ( ) + QFileInfo ( file ) . baseName ( ) ,
true , retries ) ;
script . log ( " SUCCESS: " + file ) ;
}
} catch ( std : : exception & e ) {
failed = true ;
script . log ( " FAILED: " + file + " with " + e . what ( ) ) ;
xml : : Node failure ( " failure " ) ;
failure . attr ( " message " ) = Script : : xmlattr ( e . what ( ) ) . toStdString ( ) ;
testsuites - > last ( ) . last ( ) < < failure ;
if ( expectedtestcases = = - 1 )
testsuites - > last ( ) . attr ( " failures " ) = " 1 " ;
else
testsuites - > last ( ) . attr ( " failures " ) =
mrw : : string ( expectedtestcases + 1 - testsuites - > last ( ) . children ( ) ) ;
if ( parser . isSet ( " skipped " ) ) {
testcase . attr ( " name " ) = " skipped test case " ;
failure . attr ( " message " ) = " skipped due to previous failure " ;
testcase < < failure ;
for ( int i ( testsuites - > last ( ) . children ( ) ) ; i < expectedtestcases ; + + i )
testsuites - > last ( ) < < testcase ;
}
}
if ( expectedtestcases = = - 1 ) {
testsuites - > last ( ) . attr ( " tests " ) =
mrw : : string ( testsuites - > last ( ) . children ( ) ) ;
} else {
testsuites - > last ( ) . attr ( " tests " ) = mrw : : string ( expectedtestcases ) ;
}
if ( script . cout ( ) . size ( ) )
testsuites - > last ( ) < < ( xml : : String ( " system-out " ) =
script . xmlattr ( script . cout ( ) ) . toStdString ( ) ) ;
if ( script . cerr ( ) . size ( ) )
testsuites - > last ( ) < < ( xml : : String ( " system-err " ) =
script . xmlattr ( script . cerr ( ) ) . toStdString ( ) ) ;
}
if ( parser . isSet ( " xml " ) ) { // todo: write xml file
std : : ofstream xmlfile ( parser . value ( " xml " ) . toStdString ( ) ) ;
xmlfile < < ( * testsuites ) < < std : : endl ;
}
return failed ? 1 : 0 ;
} catch ( std : : exception & e ) {
return 1 ;
}