new commands testsuite and testcase
This commit is contained in:
@@ -1,3 +1,10 @@
|
||||
2015-05-08 15:34 marc
|
||||
|
||||
* ChangeLog, ax_init_standard_project.m4, bootstrap.sh,
|
||||
docker/Dockerfile, src/commands.hxx, src/exceptions.hxx,
|
||||
src/testgui.hxx: new feature: if tatement for conditions in
|
||||
functions
|
||||
|
||||
2015-05-06 23:09 marc
|
||||
|
||||
* ChangeLog, bootstrap.sh, doc/doxyfile.in,
|
||||
|
@@ -104,12 +104,24 @@ AC_DEFUN([AX_CHECK_QT], [
|
||||
MOC_FLAGS+=" -DHAVE_$1=1 ${[$1]5_CFLAGS}"
|
||||
AM_CXXFLAGS+=" ${[$1]5_CFLAGS}"
|
||||
LIBS+=" ${[$1]5_LIBS}"
|
||||
modules=${qt_modules//Qt/Qt5}
|
||||
if test -z "$PKG_REQUIREMENTS"; then
|
||||
PKG_REQUIREMENTS="${modules// /, }"
|
||||
else
|
||||
PKG_REQUIREMENTS="${PKG_REQUIREMENTS}, ${modules// /, }"
|
||||
fi
|
||||
if test -n "${qt_modules_optional}"; then
|
||||
PKG_CHECK_MODULES([$1]5_OPTIONAL, [${qt_modules_optional//Qt/Qt5}], [
|
||||
AM_CPPFLAGS+=" ${[$1]5_OPTIONAL_CFLAGS}"
|
||||
MOC_FLAGS+=" ${[$1]5_OPTIONAL_CFLAGS}"
|
||||
AM_CXXFLAGS+=" ${[$1]5_OPTIONAL_CFLAGS}"
|
||||
LIBS+=" ${[$1]5_OPTIONAL_LIBS}"
|
||||
modules=${qt_modules_optional//Qt/Qt5}
|
||||
if test -z "$PKG_REQUIREMENTS"; then
|
||||
PKG_REQUIREMENTS="${modules// /, }"
|
||||
else
|
||||
PKG_REQUIREMENTS="${PKG_REQUIREMENTS}, ${modules// /, }"
|
||||
fi
|
||||
], [
|
||||
AC_MSG_NOTICE([Not found: ${qt_modules_optional//Qt/Qt5}])
|
||||
])
|
||||
@@ -122,12 +134,24 @@ AC_DEFUN([AX_CHECK_QT], [
|
||||
MOC_FLAGS+=" -DHAVE_$1=1 ${$1_CFLAGS}"
|
||||
AM_CXXFLAGS+=" ${$1_CFLAGS}"
|
||||
LIBS+=" ${$1_LIBS}"
|
||||
modules=${qt_modules}
|
||||
if test -z "$PKG_REQUIREMENTS"; then
|
||||
PKG_REQUIREMENTS="${modules// /, }"
|
||||
else
|
||||
PKG_REQUIREMENTS="${PKG_REQUIREMENTS}, ${modules// /, }"
|
||||
fi
|
||||
if test -n "$3"; then
|
||||
PKG_CHECK_MODULES($1_OPTIONAL, [${qt_modules_optional}], [
|
||||
AM_CPPFLAGS+=" ${$1_OPTIONAL_CFLAGS}"
|
||||
MOC_FLAGS+=" ${$1_OPTIONAL_CFLAGS}"
|
||||
AM_CXXFLAGS+=" ${$1_OPTIONAL_CFLAGS}"
|
||||
LIBS+=" ${$1_OPTIONAL_LIBS}"
|
||||
modules=${qt_modules_optional}
|
||||
if test -z "$PKG_REQUIREMENTS"; then
|
||||
PKG_REQUIREMENTS="${modules// /, }"
|
||||
else
|
||||
PKG_REQUIREMENTS="${PKG_REQUIREMENTS}, ${modules// /, }"
|
||||
fi
|
||||
], [
|
||||
AC_MSG_NOTICE([Not found: ${qt_modules_optional}])
|
||||
])
|
||||
@@ -139,6 +163,7 @@ AC_DEFUN([AX_CHECK_QT], [
|
||||
AC_SUBST(AM_CPPFLAGS)
|
||||
AC_SUBST(MOC_FLAGS)
|
||||
AC_SUBST(AM_CXXFLAGS)
|
||||
AC_SUBST(PKG_REQUIREMENTS)
|
||||
AX_ADDITIONAL_QT_RULES_HACK='
|
||||
ui_%.hxx: %.ui
|
||||
$(UIC) -o [$][@] $<
|
||||
@@ -191,3 +216,11 @@ AC_DEFUN([AX_REQUIRE_QT], [
|
||||
AC_MSG_ERROR([Required Qt modules not found: $2])
|
||||
fi
|
||||
])
|
||||
|
||||
|
||||
# Omit Qt Keywords
|
||||
# AX_QT_NO_KEYWORDS
|
||||
|
||||
AC_DEFUN([AX_QT_NO_KEYWORDS], [
|
||||
AM_CPPFLAGS+=" -DQT_NO_KEYWORDS"
|
||||
])
|
||||
|
@@ -5,7 +5,7 @@
|
||||
|
||||
# define least version number from subversion's revision number:
|
||||
# it is taken modulo 256 due to a bug on Apple's MacOSX
|
||||
m4_define(x_version, m4_esyscmd_s(
|
||||
m4_define(x_least, m4_esyscmd_s(
|
||||
SVN_REVISION="ERROR-UNDEFINED-REVISION-to-be-built-in-subdirectory-of-svn-checkout"
|
||||
for path in . .. ../..; do
|
||||
if svn info $path 2>&1 > /dev/null; then
|
||||
@@ -15,7 +15,26 @@ m4_define(x_version, m4_esyscmd_s(
|
||||
fi
|
||||
done
|
||||
# Mac does not support LEAST > 255
|
||||
echo $ECHO_N x_major.$((x_minor+$SVN_REVISION/256)).$(($SVN_REVISION%256))
|
||||
echo $ECHO_N $(($SVN_REVISION%256))
|
||||
))
|
||||
# define version number from subversion's revision number:
|
||||
# it is taken modulo 256 due to a bug on Apple's MacOSX
|
||||
# add to x_minor if revision number is > 256
|
||||
m4_define(x_minor_fixed, m4_esyscmd_s(
|
||||
SVN_REVISION="ERROR-UNDEFINED-REVISION-to-be-built-in-subdirectory-of-svn-checkout"
|
||||
for path in . .. ../..; do
|
||||
if svn info $path 2>&1 > /dev/null; then
|
||||
SVN_REVISION=$(LANG= svn info $path | sed -n 's/Last Changed Rev: //p')
|
||||
(cd $path && svn2cl)
|
||||
break;
|
||||
fi
|
||||
done
|
||||
# Mac does not support LEAST > 255
|
||||
echo $ECHO_N $((x_minor+$SVN_REVISION/256))
|
||||
))
|
||||
# setup version number
|
||||
m4_define(x_version, m4_esyscmd_s(
|
||||
echo $ECHO_N x_major.x_minor_fixed.x_least
|
||||
))
|
||||
|
||||
## bugreport mail address is taken from <user@host> in first line of AUTHORS
|
||||
@@ -81,7 +100,7 @@ AC_DEFUN([AX_SUBST], [
|
||||
# m4_define(x_major, MAJOR_NUMBER) # project's major version
|
||||
# m4_define(x_minor, MINOR_NUMBER) # project's minor version
|
||||
# m4_include(ax_init_standard_project.m4)
|
||||
# AC_INIT(x_package_name, x_major.x_minor.x_least, x_bugreport, x_package_name)
|
||||
# AC_INIT(x_package_name, x_version, x_bugreport, x_package_name)
|
||||
# AM_INIT_AUTOMAKE([1.9 tar-pax])
|
||||
# AX_INIT_STANDARD_PROJECT
|
||||
#
|
||||
@@ -105,7 +124,7 @@ AC_DEFUN([AX_INIT_STANDARD_PROJECT], [
|
||||
AX_SUBST(DISTRO)
|
||||
BUILD_NUMBER=${BUILD_NUMBER:-1}
|
||||
AX_SUBST(BUILD_NUMBER)
|
||||
BUILD_DATE=$(date +"%Y-%m-%d %H:%M %Z")
|
||||
BUILD_DATE=$(LANG= date +"%a, %d %B %Y %H:%M:%S %z")
|
||||
AX_SUBST(BUILD_DATE)
|
||||
if test -f "${PROJECT_NAME}-logo.png"; then
|
||||
PROJECT_LOGO="${PROJECT_NAME}-logo.png"
|
||||
@@ -225,7 +244,7 @@ AC_DEFUN([AX_USE_LIBTOOL], [
|
||||
# libtool versioning
|
||||
LIB_MAJOR=m4_eval(x_major+x_minor)
|
||||
LIB_MINOR=x_least
|
||||
LIB_LEAST=x_minor
|
||||
LIB_LEAST=x_minor_fixed
|
||||
LIB_VERSION="${LIB_MAJOR}:${LIB_MINOR}:${LIB_LEAST}"
|
||||
AM_LDFLAGS="-version-info ${LIB_VERSION}"
|
||||
AC_SUBST(AM_LDFLAGS)
|
||||
@@ -380,6 +399,11 @@ AC_DEFUN([AX_PKG_REQUIRE], [
|
||||
AM_CPPFLAGS+=" ${$1_CFLAGS}"
|
||||
AM_CXXFLAGS+=" ${$1_CFLAGS}"
|
||||
LIBS+=" ${$1_LIBS}"
|
||||
if test -z "$PKG_REQUIREMENTS"; then
|
||||
PKG_REQUIREMENTS="$2"
|
||||
else
|
||||
PKG_REQUIREMENTS="${PKG_REQUIREMENTS}, $2"
|
||||
fi
|
||||
], [
|
||||
AC_MSG_WARN([Recommended package $2 for feature $1 not installed])
|
||||
if test -n "$4"; then
|
||||
@@ -411,6 +435,11 @@ AC_DEFUN([AX_PKG_REQUIRE], [
|
||||
AM_CPPFLAGS+=" ${$1_CFLAGS}"
|
||||
AM_CXXFLAGS+=" ${$1_CFLAGS}"
|
||||
LIBS+=" ${$1_LIBS}"
|
||||
if test -z "$PKG_REQUIREMENTS"; then
|
||||
PKG_REQUIREMENTS="$pkg"
|
||||
else
|
||||
PKG_REQUIREMENTS="${PKG_REQUIREMENTS}, $pkg"
|
||||
fi
|
||||
], [
|
||||
AC_MSG_WARN([Recommended package $pkg for feature $1 not installed])
|
||||
])
|
||||
@@ -419,6 +448,7 @@ AC_DEFUN([AX_PKG_REQUIRE], [
|
||||
])
|
||||
AC_SUBST(AM_CPPFLAGS)
|
||||
AC_SUBST(AM_CXXFLAGS)
|
||||
AC_SUBST(PKG_REQUIREMENTS)
|
||||
if test -n "$3"; then
|
||||
old_CPPFLAGS=${CPPFLAGS}
|
||||
CPPFLAGS=" ${$1_CFLAGS} ${CPPFLAGS}"
|
||||
@@ -445,6 +475,11 @@ AC_DEFUN([AX_PKG_CHECK], [
|
||||
AM_CPPFLAGS+=" ${$1_CFLAGS}"
|
||||
AM_CXXFLAGS+=" ${$1_CFLAGS}"
|
||||
LIBS+=" ${$1_LIBS}"
|
||||
if test -z "$PKG_REQUIREMENTS"; then
|
||||
PKG_REQUIREMENTS="m4_default([$2], [$1])"
|
||||
else
|
||||
PKG_REQUIREMENTS="${PKG_REQUIREMENTS}, m4_default([$2], [$1])"
|
||||
fi
|
||||
], [
|
||||
HAVE_$1=0
|
||||
])
|
||||
@@ -452,4 +487,5 @@ AC_DEFUN([AX_PKG_CHECK], [
|
||||
AC_SUBST(HAVE_$1)
|
||||
AC_SUBST(AM_CPPFLAGS)
|
||||
AC_SUBST(AM_CXXFLAGS)
|
||||
AC_SUBST(PKG_REQUIREMENTS)
|
||||
])
|
||||
|
82
bootstrap.sh
82
bootstrap.sh
@@ -22,26 +22,29 @@ configure=0
|
||||
build=0
|
||||
overwrite=0
|
||||
rebuild=0
|
||||
rebuildfiles=()
|
||||
while test $# -gt 0; do
|
||||
case "$1" in
|
||||
(--configure|-c) configure=1;;
|
||||
(--build|-b) configure=1; build=1;;
|
||||
(--overwrite|-o) overwrite=1;;
|
||||
(--rebuild|-r) rebuild=1;;
|
||||
(--rebuild-file|-f) shift; rebuildfiles+=("$1");;
|
||||
(--version|-v)
|
||||
echo "$Id$";
|
||||
exit;;
|
||||
(--help|-h) less <<EOF
|
||||
SYNOPSIS
|
||||
|
||||
${MY_NAME} [--help|-h] [--configure|-c]
|
||||
${MY_NAME} [--help|-h] [OPTIOS]
|
||||
|
||||
OPTIONS
|
||||
|
||||
--configure, -c call ./configure after initialization
|
||||
--build, -c build, call ./configure && make after initialization
|
||||
--build, -c build, also call ./configure && make
|
||||
--overwrite, -o overwrite all basic files (bootstrap.sh, m4-macros)
|
||||
--rebuild -r force rebuild of generated files, even if modified
|
||||
--rebuild, -r force rebuild of generated files, even if modified
|
||||
--rebuild-file, -f <file> rebild specific file (can be added multiple times)
|
||||
--help, -h show this help
|
||||
--version, -v show version and date of this file
|
||||
|
||||
@@ -122,9 +125,15 @@ GENERATED FILES
|
||||
|
||||
REBUILDING FILES
|
||||
|
||||
To rebuild all these files, just run "${MY_NAME} -o". You can also
|
||||
remove and rebuild a single file by removing it from subversion,
|
||||
just call "svn rm <file>" and "${MY_NAME}" to rebuild file "<file>".
|
||||
To rebuild all these files, just run "${MY_NAME} -r".
|
||||
|
||||
To copy only the files provided by this package, that means those
|
||||
files you must never change, that means to update the build system
|
||||
to the latest release, run "${MY_NAME} -o"
|
||||
|
||||
You can also rebuild a list of singleany list of specific file files
|
||||
by adding option "${MY_NAME} -f <file>" to rebuild file
|
||||
"<file>". You can add option "-f" more than once.
|
||||
|
||||
FILES TO EDIT
|
||||
|
||||
@@ -216,7 +225,7 @@ done
|
||||
|
||||
HEADER='## @id '"\$Id\$"'
|
||||
#
|
||||
# This file has been added by '${MY_NAME}' on '$(date +"%Y-%m-%d %H:%M %Z")'
|
||||
# This file has been added by '${MY_NAME}' on '$(LANG= date +"%a, %d %B %Y %H:%M:%S %z")'
|
||||
# Feel free to change it or even remove and rebuild it, up to your needs
|
||||
#
|
||||
## 1 2 3 4 5 6 7 8
|
||||
@@ -224,6 +233,10 @@ HEADER='## @id '"\$Id\$"'
|
||||
|
||||
'
|
||||
|
||||
notice() {
|
||||
echo -e "\e[1;33m$*\e[0m"
|
||||
}
|
||||
|
||||
run() {
|
||||
check=1
|
||||
while test $# -gt 0; do
|
||||
@@ -256,6 +269,12 @@ testtag() {
|
||||
egrep -q '^ *'"$1" configure.ac
|
||||
}
|
||||
|
||||
contains() {
|
||||
local e
|
||||
for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
|
||||
return 1
|
||||
}
|
||||
|
||||
checkdir() {
|
||||
if ! test -d "$1"; then # create path
|
||||
run mkdir -p "$1"
|
||||
@@ -285,8 +304,10 @@ to() {
|
||||
esac
|
||||
shift;
|
||||
done
|
||||
if checkfile "$1" && test $rebuild -eq 0; then # file already exists
|
||||
return
|
||||
if checkfile "$1" && test $rebuild -eq 0 \
|
||||
&& ! contains "$1" "${rebuildfiles[@]}"; then
|
||||
# file already exists and must not be rebuilt
|
||||
return 1
|
||||
fi
|
||||
checkdir "$(dirname ${1})"
|
||||
echo -en "\e[1m-> generating:\e[0m $1 ..."
|
||||
@@ -307,10 +328,13 @@ to() {
|
||||
run svn add "$1"
|
||||
run svn propset svn:keywords "Id" "$1"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
copy() {
|
||||
if checkfile "$1" && test $overwrite -eq 0; then
|
||||
if checkfile "$1" && test $overwrite -eq 0 \
|
||||
&& ! contains "$1" "${rebuildfiles[@]}"; then
|
||||
# file already exists and must not be rebuilt
|
||||
return
|
||||
fi
|
||||
run cp "${0%/*}/$1" "$1"
|
||||
@@ -347,14 +371,18 @@ copy ${MY_NAME}
|
||||
copy ax_init_standard_project.m4
|
||||
copy ax_cxx_compile_stdcxx_11.m4
|
||||
copy ax_check_qt.m4
|
||||
copy AUTHORS
|
||||
if ! test -f configure.ac; then
|
||||
to configure.ac <<EOF
|
||||
AUTHOR=$(gpg -K | sed -n 's,uid *,,p' | sort | head -1)
|
||||
if test -z "${AUTHOR}"; then
|
||||
AUTHOR="FIRSTNAME LASTNAME (URL) <EMAIL>"
|
||||
fi
|
||||
echo "$AUTHOR" | to AUTHORS && notice "please edit AUTHORS"
|
||||
to configure.ac <<EOF && \
|
||||
( notice "please edit configure.ac, then rerun $0"; exit 0 )
|
||||
${HEADER}m4_define(x_package_name, ${DEFAULT_PROJECT_NAME}) # project's name
|
||||
m4_define(x_major, 0) # project's major version
|
||||
m4_define(x_minor, 0) # project's minor version
|
||||
m4_include(ax_init_standard_project.m4)
|
||||
AC_INIT(x_package_name, x_major.x_minor.x_least, x_bugreport, x_package_name)
|
||||
AC_INIT(x_package_name, x_version, x_package_name)
|
||||
AM_INIT_AUTOMAKE([1.9 tar-pax])
|
||||
AX_INIT_STANDARD_PROJECT
|
||||
|
||||
@@ -370,14 +398,11 @@ AX_INIT_STANDARD_PROJECT
|
||||
|
||||
# qt features, uncomment, what you need:
|
||||
#AX_CHECK_QT([QT], [QtCore QtGui QtNetwork], [QtWidgets])
|
||||
#AM_CPPFLAGS="${AM_CPPFLAGS} -DQT_NO_KEYWORDS"
|
||||
#AX_QT_NO_KEYWORDS
|
||||
|
||||
# create output
|
||||
AC_OUTPUT
|
||||
EOF
|
||||
echo "please edit configure.ac, then rerun $0"
|
||||
exit 0
|
||||
fi
|
||||
PACKAGE_NAME=$(sed -n 's/.*m4_define *( *x_package_name *, *\([^ ]*\) *).*/\1/p' configure.ac)
|
||||
echo "${HEADER}MAINTAINERCLEANFILES = makefile.in" | to --condition AX_USE_CXX src/makefile.am
|
||||
to --condition AX_USE_CXX src/version.hxx <<EOF
|
||||
@@ -473,10 +498,14 @@ LDADD = -l${PACKAGE_NAME}
|
||||
|
||||
MAINTAINERCLEANFILES = makefile.in
|
||||
EOF
|
||||
if testtag AX_USE_DOXYGEN && ! test -f doc/doxyfile.in; then
|
||||
if testtag AX_USE_DOXYGEN && \
|
||||
( ! checkfile doc/doxyfile.in || \
|
||||
contains doc/doxyfile.in "${rebuildfiles[@]}" ); then
|
||||
run doxygen -g doc/doxyfile.in
|
||||
if test $exists -eq 0; then
|
||||
run svn add doc/doxyfile.in
|
||||
run svn propset svn:keywords "Id" doc/doxyfile.in
|
||||
fi
|
||||
doxyreplace PROJECT_NAME "@PACKAGE_NAME@"
|
||||
doxyreplace PROJECT_NUMBER "@PACKAGE_VERSION@"
|
||||
doxyreplace PROJECT_BRIEF "@DESCRIPTION@"
|
||||
@@ -595,13 +624,15 @@ ${HEADER}%:
|
||||
EOF
|
||||
echo 7 | to debian/compat
|
||||
fi
|
||||
if ! test -f makefile.am; then
|
||||
SUBDIRS=""
|
||||
for d in src test scripts doc examples; do
|
||||
SUBDIRS=""
|
||||
for d in src test scripts doc examples; do
|
||||
test -d $d && SUBDIRS="${SUBDIRS} $d"
|
||||
done
|
||||
echo "${HEADER}SUBDIRS =${SUBDIRS}\n\nMAINTAINERCLEANFILES = makefile.in" | to makefile.am
|
||||
fi
|
||||
done
|
||||
to makefile.am<<EOF
|
||||
${HEADER}SUBDIRS =${SUBDIRS}
|
||||
|
||||
MAINTAINERCLEANFILES = makefile.in
|
||||
EOF
|
||||
to --condition AX_USE_LIBTOOL src/${PACKAGE_NAME}.pc.in <<EOF
|
||||
${HEADER}prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
@@ -613,6 +644,7 @@ Description: @DESCRIPTION@
|
||||
Version: @VERSION@
|
||||
Libs: -L\${libdir} -l@PACKAGE_NAME@ @LDFLAGS@
|
||||
Cflags: -I\${includedir} @CPPFLAGS@
|
||||
Requires: @PKG_REQUIREMENTS@
|
||||
EOF
|
||||
|
||||
#### Cleanup If Makefile Exists ####
|
||||
|
9
doc/makefile.amto
Normal file
9
doc/makefile.amto
Normal file
@@ -0,0 +1,9 @@
|
||||
## @id $Id$
|
||||
#
|
||||
# This file has been added by bootstrap.sh on Sat, 09 May 2015 14:15:27 +0200
|
||||
# Feel free to change it or even remove and rebuild it, up to your needs
|
||||
#
|
||||
## 1 2 3 4 5 6 7 8
|
||||
## 45678901234567890123456789012345678901234567890123456789012345678901234567890
|
||||
|
||||
MAINTAINERCLEANFILES = makefile.in
|
151
src/commands.hxx
151
src/commands.hxx
@@ -462,35 +462,45 @@ class Script: public QObject {
|
||||
linenr+=oldsize-in.size())
|
||||
_script.push_back(parse(in, linenr));
|
||||
}
|
||||
void run(QWebFrame* frame, QString td = QString(), bool screenshots = true,
|
||||
int maxretries = 0) {
|
||||
assert(_internalTestsuiteNode);
|
||||
run(frame, *_internalTestsuiteNode, td,
|
||||
screenshots, maxretries); //( @todo extract from parent
|
||||
QStringList print() {
|
||||
QStringList result;
|
||||
for (auto cmd(_script.begin()); cmd!=_script.end(); ++cmd) {
|
||||
result += (*cmd)->command();
|
||||
}
|
||||
void run(QWebFrame* frame, xml::Node& testsuiteNode,
|
||||
return result;
|
||||
}
|
||||
void run(QWebFrame* frame) {
|
||||
run(frame, _testsuites, targetdir(), _screenshots, _maxretries);
|
||||
}
|
||||
void run(QWebFrame* frame, std::shared_ptr<xml::Node> testsuites,
|
||||
QString td = QString(), bool screenshots = true,
|
||||
int maxretries = 0) {
|
||||
_internalTestsuiteNode = &testsuiteNode;
|
||||
assert(_internalTestsuiteNode);
|
||||
_testsuites = testsuites;
|
||||
_timeout = 20; // defaults to 20s
|
||||
_ignoreSignalsUntil.clear();
|
||||
addSignals(frame);
|
||||
_screenshots = screenshots;
|
||||
_maxretries = maxretries;
|
||||
_timer.setSingleShot(true);
|
||||
targetdir(!td.isEmpty()
|
||||
? td
|
||||
: _testsuites->children()
|
||||
? xmlstr(_testsuites->last().attr("name"))
|
||||
: "attachments");
|
||||
if (!_testsuites->children()) {
|
||||
xml::Node testsuite("testsuite");
|
||||
testsuite.attr("name") = "Unnamed Test Suite";
|
||||
(*_testsuites)<<testsuite;
|
||||
}
|
||||
int retries(0), back(0);
|
||||
for (auto cmd(_script.begin()); cmd!=_script.end(); ++cmd) {
|
||||
xml::Node testcase("testcase");
|
||||
try {
|
||||
testcase.attr("classname") =
|
||||
testsuiteNode.attr("name");
|
||||
//xmlattr((*cmd)->command(), true).toStdString();
|
||||
testcase.attr("classname") = xmlattr(_testclass, true).toStdString();
|
||||
testcase.attr("name") =
|
||||
xmlattr((*cmd)->tag(), true).toStdString();
|
||||
xmlattr((*cmd)->command(), true).toStdString();
|
||||
if (!_ignores.size() || (*cmd)->tag()=="label") { // not ignored
|
||||
_timer.start(_timeout*1000);
|
||||
testsuite(xmlstr(testsuiteNode.attr("name")));
|
||||
targetdir(!td.isEmpty() ? td : xmlstr(testsuiteNode.attr("name")));
|
||||
try {
|
||||
if (!(*cmd)->execute(this, frame)) {
|
||||
_timer.stop();
|
||||
@@ -501,7 +511,7 @@ class Script: public QObject {
|
||||
xmlattr(_cerr).toStdString());
|
||||
_cout.clear();
|
||||
_cerr.clear();
|
||||
testsuiteNode<<testcase;
|
||||
_testsuites->last()<<testcase;
|
||||
break; // test is successfully finished
|
||||
}
|
||||
} catch (PossibleRetryLoad& e) {
|
||||
@@ -511,7 +521,7 @@ class Script: public QObject {
|
||||
try { // take a screenshot on error
|
||||
QString filename(Screenshot::screenshot
|
||||
((*cmd)->line(), targetdir(),
|
||||
QFileInfo(testsuite()).baseName(),
|
||||
_testclass,
|
||||
QString("retry-%1")
|
||||
.arg((ulong)retries, 2, 10,
|
||||
QLatin1Char('0')),
|
||||
@@ -563,7 +573,7 @@ class Script: public QObject {
|
||||
xmlattr(_cerr).toStdString());
|
||||
_cout.clear();
|
||||
_cerr.clear();
|
||||
testsuiteNode<<testcase;
|
||||
_testsuites->last()<<testcase;
|
||||
}
|
||||
} catch (Exception& e) {
|
||||
_timer.stop();
|
||||
@@ -574,7 +584,7 @@ class Script: public QObject {
|
||||
xmlattr(_cerr).toStdString());
|
||||
_cout.clear();
|
||||
_cerr.clear();
|
||||
testsuiteNode<<testcase;
|
||||
_testsuites->last()<<testcase;
|
||||
removeSignals(frame);
|
||||
e.line((*cmd)->line());
|
||||
if (screenshots)
|
||||
@@ -582,22 +592,20 @@ class Script: public QObject {
|
||||
{
|
||||
QString filename(Screenshot::sourceHtml
|
||||
((*cmd)->line(), targetdir(),
|
||||
QFileInfo(testsuite()).baseName(),
|
||||
_testclass,
|
||||
"error", frame));
|
||||
plainlog("[[ATTACHMENT|"+filename+"]]");
|
||||
} {
|
||||
QString filename(Screenshot::screenshot
|
||||
((*cmd)->line(), targetdir(),
|
||||
QFileInfo(testsuite()).baseName(),
|
||||
_testclass,
|
||||
"error", frame));
|
||||
plainlog("[[ATTACHMENT|"+filename+"]]");
|
||||
}
|
||||
} catch (... ) {} // ignore exception in screenshot
|
||||
_internalTestsuiteNode = 0;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
_internalTestsuiteNode = 0;
|
||||
removeSignals(frame);
|
||||
if (!_signals.empty()) throw UnhandledSignals(_signals);
|
||||
}
|
||||
@@ -613,18 +621,26 @@ class Script: public QObject {
|
||||
bool screenshots() {
|
||||
return _screenshots;
|
||||
}
|
||||
void testsuite(QString name) {
|
||||
_testsuite = name;
|
||||
}
|
||||
QString testsuite() {
|
||||
return _testsuite;
|
||||
}
|
||||
void targetdir(QString name) {
|
||||
_targetdir = name;
|
||||
}
|
||||
QString targetdir() {
|
||||
return _targetdir;
|
||||
}
|
||||
void testclass(QString tc) {
|
||||
_testclass = tc;
|
||||
}
|
||||
QString testclass() {
|
||||
return _testclass;
|
||||
}
|
||||
void testsuite(QString name) {
|
||||
xml::Node testsuite("testsuite");
|
||||
testsuite.attr("name") = xmlattr(name, true).toStdString();
|
||||
testsuite.attr("timestamp") =
|
||||
QDateTime::currentDateTime().toString(Qt::ISODate).toStdString();
|
||||
_testsuites->last().attr("failures") = "0";
|
||||
(*_testsuites)<<testsuite;
|
||||
}
|
||||
Signal getSignal() {
|
||||
while (!_signals.size()) QCoreApplication::processEvents();
|
||||
Signal res(_signals.front());
|
||||
@@ -655,10 +671,11 @@ class Script: public QObject {
|
||||
_rvariables = o._rvariables;
|
||||
_timeout = o._timeout;
|
||||
_clicktype = o._clicktype;
|
||||
_testsuite = o._testsuite;
|
||||
_internalTestsuiteNode = o._internalTestsuiteNode;
|
||||
assert(_internalTestsuiteNode);
|
||||
_testsuites = o._testsuites;
|
||||
_testclass = o._testclass;
|
||||
_targetdir = o._targetdir;
|
||||
_maxretries = o._maxretries;
|
||||
_screenshots = o._screenshots;
|
||||
_cout.clear();
|
||||
_cerr.clear();
|
||||
_ignoreSignalsUntil.clear();
|
||||
@@ -835,15 +852,16 @@ class Script: public QObject {
|
||||
QString _cout;
|
||||
QString _cerr;
|
||||
bool _screenshots;
|
||||
int _maxretries;
|
||||
QString _ignoreSignalsUntil;
|
||||
QMap<QString, QString> _variables; ///< variable mapping
|
||||
QMap<LenString, LenString> _rvariables; ///< reverse variable mapping
|
||||
QMap<QString, std::shared_ptr<Function> > _functions;
|
||||
int _timeout;
|
||||
ClickType _clicktype;
|
||||
QString _testsuite;
|
||||
QString _targetdir;
|
||||
xml::Node* _internalTestsuiteNode; ///< only valid within run
|
||||
std::shared_ptr<xml::Node> _testsuites; ///< only valid within run
|
||||
QString _testclass;
|
||||
};
|
||||
|
||||
class Do: public Command {
|
||||
@@ -1892,7 +1910,8 @@ class If: public Command {
|
||||
"Match allows a regular expression.";
|
||||
}
|
||||
QString command() const {
|
||||
return tag();
|
||||
return tag()+" "+_variable+" "+QString(_cmp)+" "+_value
|
||||
+(_script.get()?"\n"+_script->print().join("\n "):"");
|
||||
}
|
||||
std::shared_ptr<Command> parse(Script*, QString args,
|
||||
QStringList& in, int) {
|
||||
@@ -1934,6 +1953,64 @@ class If: public Command {
|
||||
std::shared_ptr<Script> _script;
|
||||
};
|
||||
|
||||
class TestSuite: public Command {
|
||||
public:
|
||||
QString tag() const {
|
||||
return "testsuite";
|
||||
}
|
||||
QString description() const {
|
||||
return
|
||||
tag()+" <name>"
|
||||
"\n\n"
|
||||
"Start a testsuite and give it a name.";
|
||||
}
|
||||
QString command() const {
|
||||
return tag()+" "+_name;
|
||||
}
|
||||
std::shared_ptr<Command> parse(Script*, QString args,
|
||||
QStringList&, int) {
|
||||
std::shared_ptr<TestSuite> cmd(new TestSuite());
|
||||
cmd->_name = args;
|
||||
return cmd;
|
||||
}
|
||||
bool execute(Script* script, QWebFrame* frame) {
|
||||
Logger log(this, script);
|
||||
script->testsuite(script->replacevars(_name));
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
QString _name;
|
||||
};
|
||||
|
||||
class TestCase: public Command {
|
||||
public:
|
||||
QString tag() const {
|
||||
return "testcase";
|
||||
}
|
||||
QString description() const {
|
||||
return
|
||||
tag()+" <name>"
|
||||
"\n\n"
|
||||
"Start a testcase and give it a name.";
|
||||
}
|
||||
QString command() const {
|
||||
return tag()+" "+_name;
|
||||
}
|
||||
std::shared_ptr<Command> parse(Script*, QString args,
|
||||
QStringList&, int) {
|
||||
std::shared_ptr<TestCase> cmd(new TestCase());
|
||||
cmd->_name = args;
|
||||
return cmd;
|
||||
}
|
||||
bool execute(Script* script, QWebFrame* frame) {
|
||||
Logger log(this, script);
|
||||
script->testclass(script->replacevars(_name));
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
QString _name;
|
||||
};
|
||||
|
||||
/* Template:
|
||||
class : public Command {
|
||||
public:
|
||||
@@ -1965,7 +2042,7 @@ inline bool Screenshot::execute(Script* script, QWebFrame* frame) {
|
||||
if (!script->screenshots()) return true;
|
||||
Logger log(this, script);
|
||||
QString filename(screenshot(line(), script->targetdir(),
|
||||
QFileInfo(script->testsuite()).baseName(),
|
||||
script->testclass(),
|
||||
script->replacevars(_filename), frame));
|
||||
log["[[ATTACHMENT|"+filename+"]]"];
|
||||
return true;
|
||||
@@ -2004,7 +2081,7 @@ inline void Command::subScript(std::shared_ptr<Script> script,
|
||||
connect(script.get(), SIGNAL(logging(QString)),
|
||||
parent, SLOT(log(QString)));
|
||||
parent->removeSignals(frame);
|
||||
script->run(frame, parent->targetdir());
|
||||
script->run(frame);
|
||||
parent->addSignals(frame);
|
||||
disconnect(script.get(), SIGNAL(logging(QString)),
|
||||
parent, SLOT(log(QString)));
|
||||
@@ -2042,6 +2119,8 @@ inline void Script::initPrototypes() {
|
||||
add(new Function);
|
||||
add(new Call);
|
||||
add(new If);
|
||||
add(new TestSuite);
|
||||
add(new TestCase);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -117,23 +117,17 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
|
||||
try {
|
||||
Script script;
|
||||
connect(&script, SIGNAL(logging(QString)), SLOT(logging(QString)));
|
||||
xml::Node testsuite("testsuite");
|
||||
std::shared_ptr<xml::Node> testsuites(new xml::Node("testsuite"));
|
||||
if (_setupscriptactive->isEnabled()
|
||||
&& _setupscriptactive->isChecked()) {
|
||||
script.parse(_setupscript->toPlainText().split('\n'));
|
||||
testsuite.attr("name") = "setup-script";
|
||||
testsuite.attr("timestamp") =
|
||||
QDateTime::currentDateTime().toString(Qt::ISODate).toStdString();
|
||||
script.run(_web->page()->mainFrame(), testsuite, QString(), false);
|
||||
script.run(_web->page()->mainFrame(), testsuites, QString(), false);
|
||||
script.reset();
|
||||
}
|
||||
QString text(_testscript->textCursor().selection().toPlainText());
|
||||
if (text.isEmpty()) text = _testscript->toPlainText();
|
||||
script.parse(text.split('\n'));
|
||||
testsuite.attr("name") = "setup-script";
|
||||
testsuite.attr("timestamp") =
|
||||
QDateTime::currentDateTime().toString(Qt::ISODate).toStdString();
|
||||
script.run(_web->page()->mainFrame(), testsuite, QString(), false);
|
||||
script.run(_web->page()->mainFrame(), testsuites, QString(), false);
|
||||
} catch (std::exception &x) {
|
||||
QMessageBox::critical(this, tr("Script Failed"),
|
||||
tr("Script failed with message:\n%1")
|
||||
@@ -222,17 +216,14 @@ class TestGUI: public QMainWindow, protected Ui::TestGUI {
|
||||
_setupscriptactive->setEnabled(false);
|
||||
try {
|
||||
_setupscriptstatus->setText(trUtf8("?"));
|
||||
xml::Node testsuite("testsuite");
|
||||
testsuite.attr("name") = "setup-script";
|
||||
testsuite.attr("timestamp") =
|
||||
QDateTime::currentDateTime().toString(Qt::ISODate).toStdString();
|
||||
std::shared_ptr<xml::Node> testsuites(new xml::Node("testsuite"));
|
||||
Script script;
|
||||
TestWebPage page(0, true);
|
||||
script.parse(_setupscript->toPlainText().split('\n'));
|
||||
script.run(page.mainFrame(), testsuite, QString(), false);
|
||||
script.run(page.mainFrame(), testsuites, QString(), false);
|
||||
_setupScript.cleanup();
|
||||
_setupScript.parse(_setupscript->toPlainText().split('\n'));
|
||||
_setupScript.run(page.mainFrame(), testsuite, QString(), false);;
|
||||
_setupScript.run(page.mainFrame(), testsuites, QString(), false);;
|
||||
_setupscriptstatus->setText(trUtf8("✔"));
|
||||
_setupscriptactive->setEnabled(true);
|
||||
} catch (std::exception &x) {
|
||||
|
@@ -16,6 +16,42 @@
|
||||
#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 = 60) {
|
||||
QStringList res;
|
||||
QStringList lines(txt.split('\n'));
|
||||
@@ -102,7 +138,7 @@ int main(int argc, char *argv[]) try {
|
||||
int height(parser.value("height").toInt());
|
||||
QString target(parser.value("target-path"));
|
||||
p.resize(width, height);
|
||||
xml::Node testsuites("testsuites");
|
||||
std::shared_ptr<xml::Node> testsuites(new xml::Node("testsuites"));
|
||||
Q_FOREACH(QString file, parser.positionalArguments()) {
|
||||
int expectedtestcases(-1);
|
||||
xml::Node testsuite("testsuite");
|
||||
@@ -142,47 +178,48 @@ int main(int argc, char *argv[]) try {
|
||||
testsuite<<testcase;
|
||||
}
|
||||
} else {
|
||||
script.run(p.page()->mainFrame(), testsuite,
|
||||
testsuite.attr("failures") = "0";
|
||||
(*testsuites)<<testsuite;
|
||||
script.run(p.page()->mainFrame(), testsuites,
|
||||
target+QDir::separator()+QFileInfo(file).baseName(),
|
||||
true, retries);
|
||||
testsuite.attr("failures") = "0";
|
||||
script.log("SUCCESS: "+file);
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
script.log("FAILED: "+file+" with "+e.what());
|
||||
xml::Node failure("failure");
|
||||
failure.attr("message") = Script::xmlattr(e.what()).toStdString();
|
||||
testsuite[testsuite.children()-1]<<failure;
|
||||
testsuites->last().last()<<failure;
|
||||
if (expectedtestcases==-1)
|
||||
testsuite.attr("failures") = "1";
|
||||
testsuites->last().attr("failures") = "1";
|
||||
else
|
||||
testsuite.attr("failures") =
|
||||
mrw::string(expectedtestcases+1-testsuite.children());
|
||||
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(testsuite.children()); i<expectedtestcases; ++i)
|
||||
testsuite<<testcase;
|
||||
for (int i(testsuites->last().children()); i<expectedtestcases; ++i)
|
||||
testsuites->last()<<testcase;
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
if (expectedtestcases==-1) {
|
||||
testsuite.attr("tests") = mrw::string(testsuite.children());
|
||||
testsuites->last().attr("tests") =
|
||||
mrw::string(testsuites->last().children());
|
||||
} else {
|
||||
testsuite.attr("tests") = mrw::string(expectedtestcases);
|
||||
testsuites->last().attr("tests") = mrw::string(expectedtestcases);
|
||||
}
|
||||
if (script.cout().size())
|
||||
testsuite<<(xml::String("system-out") =
|
||||
testsuites->last()<<(xml::String("system-out") =
|
||||
script.xmlattr(script.cout()).toStdString());
|
||||
if (script.cerr().size())
|
||||
testsuite<<(xml::String("system-err") =
|
||||
testsuites->last()<<(xml::String("system-err") =
|
||||
script.xmlattr(script.cerr()).toStdString());
|
||||
testsuites<<testsuite;
|
||||
}
|
||||
if (parser.isSet("xml")) { // todo: write xml file
|
||||
std::ofstream xmlfile(parser.value("xml").toStdString());
|
||||
xmlfile<<testsuites<<std::endl;
|
||||
xmlfile<<(*testsuites)<<std::endl;
|
||||
}
|
||||
return failed ? 1 : 0;
|
||||
} catch (std::exception& e) {
|
||||
|
Reference in New Issue
Block a user