new commands: include, case and fail; new emacs wt-mode for webtester files
This commit is contained in:
@@ -181,6 +181,8 @@ AC_DEFUN([AX_INIT_STANDARD_PROJECT], [
|
|||||||
_AM_SUBST_NOTMAKE([AUTHOR])
|
_AM_SUBST_NOTMAKE([AUTHOR])
|
||||||
DISTRO=$(lsb_release -sc 2>/dev/null || uname -s 2>/dev/null)
|
DISTRO=$(lsb_release -sc 2>/dev/null || uname -s 2>/dev/null)
|
||||||
AX_SUBST(DISTRO)
|
AX_SUBST(DISTRO)
|
||||||
|
ARCH=$((@<:@@<:@ $(uname -sm) =~ 64 @:>@@:>@ && echo amd64) || (@<:@@<:@ $(uname -sm) =~ 'i?86' @:>@@:>@ && echo i386 || uname -sm))
|
||||||
|
AX_SUBST(ARCH)
|
||||||
DISTRIBUTOR=$(lsb_release -si 2>/dev/null || uname -s 2>/dev/null)
|
DISTRIBUTOR=$(lsb_release -si 2>/dev/null || uname -s 2>/dev/null)
|
||||||
case "${DISTRIBUTOR// /-}" in
|
case "${DISTRIBUTOR// /-}" in
|
||||||
(Ubuntu) UBUNTU=1; AX_SUBST(UBUNTU);;
|
(Ubuntu) UBUNTU=1; AX_SUBST(UBUNTU);;
|
||||||
|
18
bootstrap.sh
18
bootstrap.sh
@@ -31,8 +31,9 @@ while test $# -gt 0; do
|
|||||||
(--configure|-c) configure=1;;
|
(--configure|-c) configure=1;;
|
||||||
(--docker|-d) docker=1;;
|
(--docker|-d) docker=1;;
|
||||||
(--build|-b) configure=1; build=1; buildtarget+=" distcheck";;
|
(--build|-b) configure=1; build=1; buildtarget+=" distcheck";;
|
||||||
(--target|-t) shift; configure=1; build=1; buildtarget+=" $1";;
|
(--all|-a) shift; configure=1; build=1; buildtarget+=" all";;
|
||||||
(--clean) shift; configure=1; build=1; buildtarget+=" maintainer-clean";;
|
(--clean) shift; configure=1; build=1; buildtarget+=" maintainer-clean";;
|
||||||
|
(--target|-t) shift; configure=1; build=1; buildtarget+=" $1";;
|
||||||
(--overwrite|-o) overwrite=1;;
|
(--overwrite|-o) overwrite=1;;
|
||||||
(--rebuild|-r) rebuild=1;;
|
(--rebuild|-r) rebuild=1;;
|
||||||
(--rebuild-file|-f) shift; rebuildfiles+=("$1");;
|
(--rebuild-file|-f) shift; rebuildfiles+=("$1");;
|
||||||
@@ -51,6 +52,8 @@ OPTIONS
|
|||||||
--configure, -c call ./configure after initialization
|
--configure, -c call ./configure after initialization
|
||||||
--docker, -d build and run tests in a docker instance
|
--docker, -d build and run tests in a docker instance
|
||||||
--build, -b build, also call ./configure && make distcheck
|
--build, -b build, also call ./configure && make distcheck
|
||||||
|
--all, -a same as -b, but make target all
|
||||||
|
--clean same as -b, but make target maintainer-clean
|
||||||
--target, -t <target> same as -b, but specify target instead of distcheck
|
--target, -t <target> same as -b, but specify target instead of distcheck
|
||||||
--overwrite, -o overwrite all basic files (bootstrap.sh, m4-macros)
|
--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
|
||||||
@@ -129,6 +132,7 @@ GENERATED FILES
|
|||||||
* resolve-debbuilddeps.sh - script to install debian package dependencies
|
* resolve-debbuilddeps.sh - script to install debian package dependencies
|
||||||
* resolve-rpmbuilddeps.sh - script to install RPM package dependencies
|
* resolve-rpmbuilddeps.sh - script to install RPM package dependencies
|
||||||
* build-in-docker.sh - script to build the project encapsulated in a docker container
|
* build-in-docker.sh - script to build the project encapsulated in a docker container
|
||||||
|
* build-in-docker.conf - additional configuration for build-in-docker.sh
|
||||||
* build-resource-file.sh - build resource.qrc file from a resource directory
|
* build-resource-file.sh - build resource.qrc file from a resource directory
|
||||||
* sql-to-dot.sed - script to convert SQL schema files to graphviz dot in doxygen
|
* sql-to-dot.sed - script to convert SQL schema files to graphviz dot in doxygen
|
||||||
* mac-create-app-bundle.sh - script to create apple mac os-x app-bundle
|
* mac-create-app-bundle.sh - script to create apple mac os-x app-bundle
|
||||||
@@ -1162,6 +1166,12 @@ Libs: -L\${libdir} -l${PACKAGE_NAME#lib} @LDFLAGS@
|
|||||||
Cflags: -I\${includedir} @CPPFLAGS@
|
Cflags: -I\${includedir} @CPPFLAGS@
|
||||||
Requires: @PKG_REQUIREMENTS@
|
Requires: @PKG_REQUIREMENTS@
|
||||||
EOF
|
EOF
|
||||||
|
to build-in-docker.conf <<EOF
|
||||||
|
repos+=("Debian|Ubuntu-precise::::::universe")
|
||||||
|
repos+=("Ubuntu-precise:::'deb http://archive.ubuntu.com/ubuntu precise universe'")
|
||||||
|
envs+=("-e 'HOME=\${HOME}'")
|
||||||
|
dirs+=("-v \${HOME}/.gnupg:\${HOME}/.gnupg:ro")
|
||||||
|
EOF
|
||||||
|
|
||||||
#### Cleanup If Makefile Exists ####
|
#### Cleanup If Makefile Exists ####
|
||||||
if test -f makefile; then
|
if test -f makefile; then
|
||||||
@@ -1177,15 +1187,15 @@ run autoconf
|
|||||||
|
|
||||||
#### Run Configure If User Requires ####
|
#### Run Configure If User Requires ####
|
||||||
if test "$configure" -eq 1; then
|
if test "$configure" -eq 1; then
|
||||||
./configure $*
|
./configure $* || exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#### Run Make If User Requires ####
|
#### Run Make If User Requires ####
|
||||||
if test "$build" -eq 1; then
|
if test "$build" -eq 1; then
|
||||||
make $buildtarget
|
make $buildtarget || exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#### Build In Docker If User Requires ####
|
#### Build In Docker If User Requires ####
|
||||||
if test "$docker" -eq 1; then
|
if test "$docker" -eq 1; then
|
||||||
./build-in-docker.sh
|
./build-in-docker.sh || exit 1
|
||||||
fi
|
fi
|
||||||
|
@@ -12,10 +12,17 @@
|
|||||||
|
|
||||||
SCHROOTNAME="$1"
|
SCHROOTNAME="$1"
|
||||||
PACKAGE_NAME=$(sed -n 's/^ *m4_define(x_package_name, \(.*\)).*/\1/p' configure.ac)
|
PACKAGE_NAME=$(sed -n 's/^ *m4_define(x_package_name, \(.*\)).*/\1/p' configure.ac)
|
||||||
|
PKGCONFIGS="${2:-epel-release}" # packages to configure yum
|
||||||
|
|
||||||
if test -n "${SCHROOTNAME}"; then
|
if test -n "${SCHROOTNAME}"; then
|
||||||
FILES=$(LANG= schroot -c ${SCHROOTNAME} -- rpmbuild -bb --clean --nobuild --define "_topdir ." --define "_sourcedir ." ${PACKAGE_NAME}.spec 2>&1 | sed -n 's, is needed by.*,,p')
|
FILES=$(LANG= schroot -c ${SCHROOTNAME} -- rpmbuild -bb --clean --nobuild --define "_topdir ." --define "_sourcedir ." ${PACKAGE_NAME}.spec 2>&1 | sed -n 's, is needed by.*,,p')
|
||||||
if test -n "${FILES}"; then
|
if test -n "${FILES}"; then
|
||||||
|
FIRST=$(echo "${FILES}" | egrep -o "${PKGCONFIGS// /|}")
|
||||||
|
if test -n "${FIRST}"; then
|
||||||
|
schroot -c ${SCHROOTNAME} -u root -- yum install -y ${FIRST} || \
|
||||||
|
schroot -c ${SCHROOTNAME} -u root -- zypper install -y ${FIRST} || \
|
||||||
|
schroot -c ${SCHROOTNAME} -u root -- dnf install -y ${FIRST}
|
||||||
|
fi
|
||||||
schroot -c ${SCHROOTNAME} -u root -- yum install -y ${FILES} || \
|
schroot -c ${SCHROOTNAME} -u root -- yum install -y ${FILES} || \
|
||||||
schroot -c ${SCHROOTNAME} -u root -- zypper install -y ${FILES} || \
|
schroot -c ${SCHROOTNAME} -u root -- zypper install -y ${FILES} || \
|
||||||
schroot -c ${SCHROOTNAME} -u root -- dnf install -y ${FILES}
|
schroot -c ${SCHROOTNAME} -u root -- dnf install -y ${FILES}
|
||||||
@@ -23,6 +30,12 @@ if test -n "${SCHROOTNAME}"; then
|
|||||||
else
|
else
|
||||||
FILES=$(LANG= rpmbuild -bb --clean --nobuild --define "_topdir ." --define "_sourcedir ." ${PACKAGE_NAME}.spec 2>&1 | sed -n 's, is needed by.*,,p')
|
FILES=$(LANG= rpmbuild -bb --clean --nobuild --define "_topdir ." --define "_sourcedir ." ${PACKAGE_NAME}.spec 2>&1 | sed -n 's, is needed by.*,,p')
|
||||||
if test -n "${FILES}"; then
|
if test -n "${FILES}"; then
|
||||||
|
FIRST=$(echo "${FILES}" | egrep -o "${PKGCONFIGS// /|}")
|
||||||
|
if test -n "${FIRST}"; then
|
||||||
|
yum install -y ${FIRST} || \
|
||||||
|
zypper install -y ${FIRST} || \
|
||||||
|
dnf install -y ${FIRST}
|
||||||
|
fi
|
||||||
yum install -y ${FILES} || \
|
yum install -y ${FILES} || \
|
||||||
zypper install -y ${FILES} || \
|
zypper install -y ${FILES} || \
|
||||||
dnf install -y ${FILES}
|
dnf install -y ${FILES}
|
||||||
|
9
scripts/90wt-mode.wt
Normal file
9
scripts/90wt-mode.wt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
;; -*-emacs-lisp-*-
|
||||||
|
;;
|
||||||
|
;; Emacs startup file for the Debian GNU/Linux wt-mode package
|
||||||
|
;;
|
||||||
|
|
||||||
|
;; Set up to autoload
|
||||||
|
(autoload 'wt-mode "wt-mode" "Major mode for editing Webtester test scripts" t)
|
||||||
|
(setq auto-mode-alist
|
||||||
|
(cons '("\\.wt\\'" . wt-mode) auto-mode-alist))
|
@@ -8,5 +8,11 @@
|
|||||||
|
|
||||||
dist_bin_SCRIPTS = doxygen-webtester.sed
|
dist_bin_SCRIPTS = doxygen-webtester.sed
|
||||||
|
|
||||||
|
emacsdir = ${datadir}/emacs/site-lisp
|
||||||
|
dist_emacs_SCRIPTS = wt-mode.el
|
||||||
|
|
||||||
|
emacsconfdir = ${sysconfdir}/emacs/site-start.d
|
||||||
|
dist_emacsconf = 90wt-mode.el
|
||||||
|
|
||||||
MAINTAINERCLEANFILES = makefile.in
|
MAINTAINERCLEANFILES = makefile.in
|
||||||
|
|
||||||
|
48
scripts/wt-mode.el
Normal file
48
scripts/wt-mode.el
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
;; Define an Emacs Mode for Webtester
|
||||||
|
;;
|
||||||
|
;; Features:
|
||||||
|
;; - indentation support
|
||||||
|
;; - syntax highlighting
|
||||||
|
;;
|
||||||
|
;; Documentations:
|
||||||
|
;; - mode tutorial:
|
||||||
|
;; https://www.emacswiki.org/emacs/ModeTutorial
|
||||||
|
;; - faces for font lock:
|
||||||
|
;; https://www.gnu.org/software/emacs/manual/html_node/elisp/Faces-for-Font-Lock.html
|
||||||
|
|
||||||
|
(defvar wt-mode-hook nil)
|
||||||
|
|
||||||
|
(defvar wt-mode-map
|
||||||
|
(let ((map (make-sparse-keymap)))
|
||||||
|
(define-key map "\C-j" 'newline-and-indent)
|
||||||
|
map)
|
||||||
|
"Newline and indent")
|
||||||
|
|
||||||
|
(add-to-list 'auto-mode-alist '("\\.wt\\'" . wt-mode))
|
||||||
|
|
||||||
|
|
||||||
|
;; get all keywords:
|
||||||
|
;; echo $(webrunner -h | sed -n 's, COMMAND: ,,p') | sed 's, ,\\\\|,g'
|
||||||
|
(defconst wt-font-lock-keywords
|
||||||
|
(list
|
||||||
|
'("^ *\\(ca-certificate\\|call\\|case\\|check\\|clear-cookies\\|click\\|clicktype\\|client-certificate\\|do\\|download\\|echo\\|execute\\|exists\\|exit\\|expect\\|fail\\|for\\|function\\|if\\|ignoreto\\|include\\|label\\|load\\|not\\|offline-storage-path\\|open\\|screenshot\\|set\\|setvalue\\|sleep\\|testcase\\|testsuite\\|timeout\\|unset\\|upload\\)" . font-lock-builtin-face)
|
||||||
|
'("^ *#.*$" . font-lock-comment-face))
|
||||||
|
"Highlighting expressions for Webtester")
|
||||||
|
|
||||||
|
(defvar wt-mode-syntax-table
|
||||||
|
(let ((st (make-syntax-table)))
|
||||||
|
st)
|
||||||
|
"Syntax table for wt-mode")
|
||||||
|
|
||||||
|
(defun wt-mode ()
|
||||||
|
"Major mode for editing Webtester test scripts"
|
||||||
|
(interactive)
|
||||||
|
(kill-all-local-variables)
|
||||||
|
(set-syntax-table wt-mode-syntax-table)
|
||||||
|
(use-local-map wt-mode-map)
|
||||||
|
(set (make-local-variable 'font-lock-defaults) '(wt-font-lock-keywords))
|
||||||
|
(setq major-mode 'wt-mode)
|
||||||
|
(setq mode-name "Webtester")
|
||||||
|
(run-hooks 'wpdl-mode-hook))
|
||||||
|
|
||||||
|
(provide 'wt-mode)
|
247
src/commands.hxx
247
src/commands.hxx
@@ -176,7 +176,8 @@ class Command: public QObject {
|
|||||||
QStringList subCommandBlock(QStringList& in) {
|
QStringList subCommandBlock(QStringList& in) {
|
||||||
QStringList commands;
|
QStringList commands;
|
||||||
int pos(-1);
|
int pos(-1);
|
||||||
while (in.size() && in[0].size() && in[0][0]==' ') {
|
while (in.size() && in[0].size() && in[0][0]==' '
|
||||||
|
&& pos<=(signed)in[0].toStdString().find_first_not_of(' ')) {
|
||||||
if (pos<0) pos=in[0].toStdString().find_first_not_of(' ');
|
if (pos<0) pos=in[0].toStdString().find_first_not_of(' ');
|
||||||
commands += in.takeFirst().mid(pos);
|
commands += in.takeFirst().mid(pos);
|
||||||
}
|
}
|
||||||
@@ -805,6 +806,9 @@ class Script: public QObject {
|
|||||||
_rvariables.remove(_variables[name]);
|
_rvariables.remove(_variables[name]);
|
||||||
_variables.remove(name);
|
_variables.remove(name);
|
||||||
}
|
}
|
||||||
|
QStringList functions() {
|
||||||
|
return _functions.keys();
|
||||||
|
}
|
||||||
void function(QString name, std::shared_ptr<Function> f) {
|
void function(QString name, std::shared_ptr<Function> f) {
|
||||||
_functions[name] = f;
|
_functions[name] = f;
|
||||||
}
|
}
|
||||||
@@ -826,7 +830,7 @@ class Script: public QObject {
|
|||||||
QString replacevars(QString txt) {
|
QString replacevars(QString txt) {
|
||||||
for(QMap<QString, QString>::iterator it(_variables.begin());
|
for(QMap<QString, QString>::iterator it(_variables.begin());
|
||||||
it!=_variables.end(); ++it)
|
it!=_variables.end(); ++it)
|
||||||
txt.replace(it.key(), it.value());
|
txt.replace(it.key(), it.value(), Qt::CaseSensitive);
|
||||||
return txt;
|
return txt;
|
||||||
}
|
}
|
||||||
QString insertvars(QString txt) {
|
QString insertvars(QString txt) {
|
||||||
@@ -834,7 +838,7 @@ class Script: public QObject {
|
|||||||
it.toBack();
|
it.toBack();
|
||||||
while (it.hasPrevious()) {
|
while (it.hasPrevious()) {
|
||||||
it.previous();
|
it.previous();
|
||||||
txt.replace(it.key(), it.value());
|
txt.replace(it.key(), it.value(), Qt::CaseSensitive);
|
||||||
}
|
}
|
||||||
return txt;
|
return txt;
|
||||||
}
|
}
|
||||||
@@ -1471,7 +1475,7 @@ class Execute: public Command {
|
|||||||
script.replaceInStrings(QRegularExpression("^"), " ");
|
script.replaceInStrings(QRegularExpression("^"), " ");
|
||||||
return tag()+" "+_command
|
return tag()+" "+_command
|
||||||
+(_args.size()?" "+_args.join(' '):QString())
|
+(_args.size()?" "+_args.join(' '):QString())
|
||||||
+(script.size()?"\n"+script.join("\n"):QString());
|
+(script.size()?"\n "+script.join("\n "):QString());
|
||||||
}
|
}
|
||||||
std::shared_ptr<Command> parse(Script*, QString args,
|
std::shared_ptr<Command> parse(Script*, QString args,
|
||||||
QStringList& in, QString, int, int) {
|
QStringList& in, QString, int, int) {
|
||||||
@@ -2096,8 +2100,8 @@ class If: public Command {
|
|||||||
"\n\n"
|
"\n\n"
|
||||||
"Execute commands conditionally. "
|
"Execute commands conditionally. "
|
||||||
"The first variant compares a variable to a value. "
|
"The first variant compares a variable to a value. "
|
||||||
"The comparision <cmp> can be = ^ . ~ < >, "
|
"The comparision <cmp> can be = ! . ^ ~ < >, "
|
||||||
"which means equal, different, contains, match, "
|
"which means equal, different, contains, contains not, match, "
|
||||||
"less (as integer), bigger (as integer). "
|
"less (as integer), bigger (as integer). "
|
||||||
"Match allows a regular expression. "
|
"Match allows a regular expression. "
|
||||||
"The second variant checks for a text in a selector, "
|
"The second variant checks for a text in a selector, "
|
||||||
@@ -2106,13 +2110,13 @@ class If: public Command {
|
|||||||
}
|
}
|
||||||
QString command() const {
|
QString command() const {
|
||||||
return tag()+" "+_variable+" "+_cmp+" "+_value
|
return tag()+" "+_variable+" "+_cmp+" "+_value
|
||||||
+(_script.get()?"\n"+_script->print().join("\n "):"");
|
+(_script.get()?"\n "+_script->print().join("\n "):"");
|
||||||
}
|
}
|
||||||
std::shared_ptr<Command> parse(Script*, QString args,
|
std::shared_ptr<Command> parse(Script*, QString args,
|
||||||
QStringList& in, QString file, int line,
|
QStringList& in, QString file, int line,
|
||||||
int indent) {
|
int indent) {
|
||||||
std::shared_ptr<If> cmd(new If());
|
std::shared_ptr<If> cmd(new If());
|
||||||
int pos(args.indexOf(QRegularExpression("[=^.~<>]")));
|
int pos(args.indexOf(QRegularExpression("[=!.^~<>]")));
|
||||||
int len(1);
|
int len(1);
|
||||||
if (args.contains("->")) {
|
if (args.contains("->")) {
|
||||||
pos = args.indexOf("->");
|
pos = args.indexOf("->");
|
||||||
@@ -2126,7 +2130,7 @@ class If: public Command {
|
|||||||
cmd->_value = args.mid(pos+len).trimmed();
|
cmd->_value = args.mid(pos+len).trimmed();
|
||||||
cmd->_script = std::shared_ptr<Script>(new Script);
|
cmd->_script = std::shared_ptr<Script>(new Script);
|
||||||
cmd->_script->parse(subCommandBlock(in), file, line+1, indent+1);
|
cmd->_script->parse(subCommandBlock(in), file, line+1, indent+1);
|
||||||
if (in.size() && in.first().contains(QRegularExpression("^else *$"))) {
|
if (in.size() && in.first().contains(QRegularExpression("^ *else *$"))) {
|
||||||
in.removeFirst();
|
in.removeFirst();
|
||||||
cmd->_else = std::shared_ptr<Script>(new Script);
|
cmd->_else = std::shared_ptr<Script>(new Script);
|
||||||
cmd->_else->parse(subCommandBlock(in), file, line+1, indent+1);
|
cmd->_else->parse(subCommandBlock(in), file, line+1, indent+1);
|
||||||
@@ -2153,10 +2157,12 @@ class If: public Command {
|
|||||||
switch (_cmp[0].toLatin1()) {
|
switch (_cmp[0].toLatin1()) {
|
||||||
case '=': check = script->variable(_variable)==value;
|
case '=': check = script->variable(_variable)==value;
|
||||||
break;
|
break;
|
||||||
case '^': check = script->variable(_variable)!=value;
|
case '!': check = script->variable(_variable)!=value;
|
||||||
break;
|
break;
|
||||||
case '.': check = script->variable(_variable).contains(value);
|
case '.': check = script->variable(_variable).contains(value);
|
||||||
break;
|
break;
|
||||||
|
case '^': check = !script->variable(_variable).contains(value);
|
||||||
|
break;
|
||||||
case '~': check =
|
case '~': check =
|
||||||
script->variable(_variable).contains(QRegularExpression(value));
|
script->variable(_variable).contains(QRegularExpression(value));
|
||||||
break;
|
break;
|
||||||
@@ -2166,7 +2172,7 @@ class If: public Command {
|
|||||||
break;
|
break;
|
||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
log(QString("evaluated expression to ")+(check?"true":"false")+":"
|
log(QString("evaluated expression to ")+(check?"true":"false")+": "
|
||||||
+script->variable(_variable)+" "+_cmp+" "+value);
|
+script->variable(_variable)+" "+_cmp+" "+value);
|
||||||
}
|
}
|
||||||
if (check) return runScript(this, _script, script, frame);
|
if (check) return runScript(this, _script, script, frame);
|
||||||
@@ -2255,8 +2261,8 @@ class Check: public Command {
|
|||||||
" output, such as <do>, which returns the result of JavaScript or"
|
" output, such as <do>, which returns the result of JavaScript or"
|
||||||
" <execute>, which returns the output of the executed command, or"
|
" <execute>, which returns the output of the executed command, or"
|
||||||
" <call>, which returns the result of the last command. "
|
" <call>, which returns the result of the last command. "
|
||||||
"The comparision <cmp> can be = ^ . ~ < >, "
|
"The comparision <cmp> can be = ! . ^ ~ < >, "
|
||||||
"which means equal, different, contains match, "
|
"which means equal, different, contains, contains not, match, "
|
||||||
"less (as integer), bigger (as integer). "
|
"less (as integer), bigger (as integer). "
|
||||||
"Match allows a regular expression. "
|
"Match allows a regular expression. "
|
||||||
" less than < (integers), larger than > (integers)";
|
" less than < (integers), larger than > (integers)";
|
||||||
@@ -2272,7 +2278,7 @@ class Check: public Command {
|
|||||||
int indent) {
|
int indent) {
|
||||||
std::shared_ptr<Check> cmd(new Check());
|
std::shared_ptr<Check> cmd(new Check());
|
||||||
cmd->_next = 0;
|
cmd->_next = 0;
|
||||||
int pos(args.indexOf(QRegularExpression("[=^.~<>]")));
|
int pos(args.indexOf(QRegularExpression("[=!.^~<>]")));
|
||||||
if (pos<0) throw BadArgument(tag()+" needs a comparision, not: "+args);
|
if (pos<0) throw BadArgument(tag()+" needs a comparision, not: "+args);
|
||||||
cmd->_value1 = args.left(pos).trimmed();
|
cmd->_value1 = args.left(pos).trimmed();
|
||||||
cmd->_cmp = args[pos].toLatin1();
|
cmd->_cmp = args[pos].toLatin1();
|
||||||
@@ -2294,8 +2300,9 @@ class Check: public Command {
|
|||||||
bool check(false);
|
bool check(false);
|
||||||
switch (_cmp) {
|
switch (_cmp) {
|
||||||
case '=': check = value1==value2; break;
|
case '=': check = value1==value2; break;
|
||||||
case '^': check = value1!=value2; break;
|
case '!': check = value1!=value2; break;
|
||||||
case '.': check = value1.contains(value2); break;
|
case '.': check = value1.contains(value2); break;
|
||||||
|
case '^': check = !value1.contains(value2); break;
|
||||||
case '~': check = value1.contains(QRegularExpression(value2)); break;
|
case '~': check = value1.contains(QRegularExpression(value2)); break;
|
||||||
case '<': check = value1.toInt()<value2.toInt(); break;
|
case '<': check = value1.toInt()<value2.toInt(); break;
|
||||||
case '>': check = value1.toInt()>value2.toInt(); break;
|
case '>': check = value1.toInt()>value2.toInt(); break;
|
||||||
@@ -2459,6 +2466,209 @@ class ClearCookies: public Command {
|
|||||||
QString _url;
|
QString _url;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Include: public Command {
|
||||||
|
public:
|
||||||
|
QString tag() const {
|
||||||
|
return "include";
|
||||||
|
}
|
||||||
|
QString description() const {
|
||||||
|
return
|
||||||
|
tag()+" <filename>"
|
||||||
|
"\n\n"
|
||||||
|
"Include a test script.";
|
||||||
|
}
|
||||||
|
QString command() const {
|
||||||
|
return tag()+" "+_filename;
|
||||||
|
}
|
||||||
|
std::shared_ptr<Command> parse(Script* script, QString args,
|
||||||
|
QStringList&, QString, int, int indent) {
|
||||||
|
std::shared_ptr<Include> cmd(new Include());
|
||||||
|
cmd->_filename = args;
|
||||||
|
QFile f(cmd->_filename);
|
||||||
|
if (!f.open(QIODevice::ReadOnly)) throw OpenIncludeFailed(cmd->_filename);
|
||||||
|
QString txt(QString::fromUtf8(f.readAll()));
|
||||||
|
try {
|
||||||
|
cmd->_script = std::shared_ptr<Script>(new Script);
|
||||||
|
cmd->_script->parse(txt.split('\n'), cmd->_filename, 1, indent+1);
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
throw ParseIncludeFailed(cmd->_filename, e.what());
|
||||||
|
}
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
bool execute(Script* script, QWebFrame* frame) try {
|
||||||
|
Logger log(this, script);
|
||||||
|
return runScript(this, _script, script, frame);
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
throw ExecuteIncludeFailed(_filename, e.what());
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
QString _filename;
|
||||||
|
std::shared_ptr<Script> _script;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Case: public Command {
|
||||||
|
public:
|
||||||
|
QString tag() const {
|
||||||
|
return "case";
|
||||||
|
}
|
||||||
|
QString description() const {
|
||||||
|
return
|
||||||
|
tag()+" <variable>\n"
|
||||||
|
" <cmp1> <value1>\n"
|
||||||
|
" <command>\n"
|
||||||
|
" <command>\n"
|
||||||
|
" <cmp1> <value2>\n"
|
||||||
|
" <command>\n"
|
||||||
|
" <command>\n"
|
||||||
|
" <...>\n"
|
||||||
|
" default\n"
|
||||||
|
" <command>\n"
|
||||||
|
" <command>\n"
|
||||||
|
" <...>\n"
|
||||||
|
"\n\n"+
|
||||||
|
tag()+" <selector>\n"
|
||||||
|
" -> <text1>\n"
|
||||||
|
" <command>\n"
|
||||||
|
" <command>\n"
|
||||||
|
" -> <text2>\n"
|
||||||
|
" <command>\n"
|
||||||
|
" <command>\n"
|
||||||
|
" <...>\n"
|
||||||
|
" default\n"
|
||||||
|
" <command>\n"
|
||||||
|
" <command>\n"
|
||||||
|
" <...>\n"
|
||||||
|
"\n\n"
|
||||||
|
"Execute commands conditionally depending on a variable. "
|
||||||
|
"It is equivalent to neested if-else-if commands."
|
||||||
|
"The first variant compares a variable to a value. "
|
||||||
|
"The comparision <cmp> can be = ! . ^ ~ < >, "
|
||||||
|
"which means equal, different, contains, contains not, match, "
|
||||||
|
"less (as integer), bigger (as integer). "
|
||||||
|
"Match allows a regular expression. "
|
||||||
|
"The second variant checks for a text in a selector, "
|
||||||
|
"similar to command exists. "
|
||||||
|
"There is an optional default part that applies if none "
|
||||||
|
"of the previous conditions match.";
|
||||||
|
}
|
||||||
|
QString command() const {
|
||||||
|
QString body;
|
||||||
|
Q_FOREACH(Condition condition, _conditions) {
|
||||||
|
body += "\n "+condition.cmp+" "+condition.value+"\n "
|
||||||
|
+condition.script->print().join("\n ");
|
||||||
|
}
|
||||||
|
return tag()+" "+_variable+body;
|
||||||
|
}
|
||||||
|
std::shared_ptr<Command> parse(Script*, QString args,
|
||||||
|
QStringList& in, QString file, int line,
|
||||||
|
int indent) {
|
||||||
|
std::shared_ptr<Case> cmd(new Case());
|
||||||
|
if (!args.size()) throw BadArgument(tag()+" requires a <variable> or <selector>");
|
||||||
|
cmd->_variable = args;
|
||||||
|
QStringList body(subCommandBlock(in));
|
||||||
|
while (body.size()) {
|
||||||
|
++line;
|
||||||
|
QStringList parts(body.takeFirst().split(' '));
|
||||||
|
QString cmp(parts.takeFirst());
|
||||||
|
QString value(parts.join(' '));
|
||||||
|
if (!cmp.contains(QRegularExpression("^[=!.^~<>]|->|default$")))
|
||||||
|
throw BadArgument(tag()+" needs a comparision, not: "+cmp);
|
||||||
|
std::shared_ptr<Script> script(std::shared_ptr<Script>(new Script));
|
||||||
|
script->parse(subCommandBlock(body), file, line+1, indent+2);
|
||||||
|
cmd->_conditions.append(Condition(cmp, value, script));
|
||||||
|
}
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
bool execute(Script* script, QWebFrame* frame) {
|
||||||
|
Logger log(this, script, false);
|
||||||
|
QString selector(script->replacevars(_variable));
|
||||||
|
Q_FOREACH(Condition condition, _conditions) {
|
||||||
|
QString value(script->replacevars(condition.value));
|
||||||
|
bool check(false);
|
||||||
|
if (condition.cmp=="default") {
|
||||||
|
log("terminate with default branch");
|
||||||
|
check = true;
|
||||||
|
} else if (condition.cmp=="->") {
|
||||||
|
Q_FOREACH(QWebElement element, frame->findAllElements(selector)) {
|
||||||
|
if (value.isEmpty() || // just find element
|
||||||
|
element.toOuterXml().indexOf(value)!=-1 ||
|
||||||
|
element.toPlainText().indexOf(value)!=-1) {
|
||||||
|
check = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log(QString("evaluated expression to ")+(check?"true":"false")+": "
|
||||||
|
+selector+" "+condition.cmp+" "+value);
|
||||||
|
} else {
|
||||||
|
switch (condition.cmp[0].toLatin1()) {
|
||||||
|
case '=': check = script->variable(_variable)==value;
|
||||||
|
break;
|
||||||
|
case '!': check = script->variable(_variable)!=value;
|
||||||
|
break;
|
||||||
|
case '.': check = script->variable(_variable).contains(value);
|
||||||
|
break;
|
||||||
|
case '^': check = !script->variable(_variable).contains(value);
|
||||||
|
break;
|
||||||
|
case '~': check =
|
||||||
|
script->variable(_variable).contains(QRegularExpression(value));
|
||||||
|
break;
|
||||||
|
case '<': check = script->variable(_variable).toInt()<value.toInt();
|
||||||
|
break;
|
||||||
|
case '>': check = script->variable(_variable).toInt()>value.toInt();
|
||||||
|
break;
|
||||||
|
default:;
|
||||||
|
}
|
||||||
|
log(QString("evaluated expression to ")+(check?"true":"false")+": "
|
||||||
|
+script->variable(_variable)+" "+condition.cmp+" "+value);
|
||||||
|
}
|
||||||
|
if (check) return runScript(this, condition.script, script, frame);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
struct Condition {
|
||||||
|
Condition(QString c, QString v, std::shared_ptr<Script> s):
|
||||||
|
cmp(c), value(v), script(s) {
|
||||||
|
}
|
||||||
|
Condition() {}
|
||||||
|
QString cmp;
|
||||||
|
QString value;
|
||||||
|
std::shared_ptr<Script> script;
|
||||||
|
};
|
||||||
|
QString _variable;
|
||||||
|
QVector<Condition> _conditions;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Fail: public Command {
|
||||||
|
public:
|
||||||
|
QString tag() const {
|
||||||
|
return "fail";
|
||||||
|
}
|
||||||
|
QString description() const {
|
||||||
|
return
|
||||||
|
tag()+" <text>"
|
||||||
|
"\n\n"
|
||||||
|
"Fail with error text.";
|
||||||
|
}
|
||||||
|
QString command() const {
|
||||||
|
return tag()+" "+_text;
|
||||||
|
}
|
||||||
|
std::shared_ptr<Command> parse(Script*, QString args,
|
||||||
|
QStringList&, QString, int, int) {
|
||||||
|
std::shared_ptr<Fail> cmd(new Fail());
|
||||||
|
cmd->_text = args;
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
bool execute(Script* script, QWebFrame*) {
|
||||||
|
Logger log(this, script);
|
||||||
|
throw TestFailed(script->replacevars(_text));
|
||||||
|
return true; // dummy
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
QString _text;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Template:
|
/* Template:
|
||||||
class : public Command {
|
class : public Command {
|
||||||
public:
|
public:
|
||||||
@@ -2544,8 +2754,10 @@ inline bool Command::runScript(Command* parentCommand,
|
|||||||
disconnect(&scriptCopy, SIGNAL(logging(QString)),
|
disconnect(&scriptCopy, SIGNAL(logging(QString)),
|
||||||
parent, SLOT(parentlog(QString)));
|
parent, SLOT(parentlog(QString)));
|
||||||
parentCommand->_result = scriptCopy.result();
|
parentCommand->_result = scriptCopy.result();
|
||||||
Q_FOREACH(QString key, scriptCopy.variables())
|
Q_FOREACH(QString key, scriptCopy.variables()) // copy new variables to parent
|
||||||
if (!vars.contains(key)) parent->set(key, scriptCopy.variable(key));
|
if (!vars.contains(key)) parent->set(key, scriptCopy.variable(key));
|
||||||
|
Q_FOREACH(QString key, scriptCopy.functions()) // copy new functions to parent
|
||||||
|
parent->function(key, scriptCopy.function(key));
|
||||||
if (parentCommand->_result.size())
|
if (parentCommand->_result.size())
|
||||||
parent->log("result: "+parentCommand->_result);
|
parent->log("result: "+parentCommand->_result);
|
||||||
return res;
|
return res;
|
||||||
@@ -2590,6 +2802,9 @@ inline void Script::initPrototypes() {
|
|||||||
add(new Echo);
|
add(new Echo);
|
||||||
add(new OfflineStoragePath);
|
add(new OfflineStoragePath);
|
||||||
add(new ClearCookies);
|
add(new ClearCookies);
|
||||||
|
add(new Include);
|
||||||
|
add(new Case);
|
||||||
|
add(new Fail);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -269,4 +269,25 @@ class CheckFailed: public TestFailed {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class OpenIncludeFailed: public TestFailed {
|
||||||
|
public:
|
||||||
|
OpenIncludeFailed(QString file):
|
||||||
|
TestFailed(QString("open include file %1 failed").arg(file)) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ParseIncludeFailed: public TestFailed {
|
||||||
|
public:
|
||||||
|
ParseIncludeFailed(QString file, QString msg):
|
||||||
|
TestFailed(QString("parse include file %1 failed with: %2").arg(file).arg(msg)) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExecuteIncludeFailed: public TestFailed {
|
||||||
|
public:
|
||||||
|
ExecuteIncludeFailed(QString file, QString msg):
|
||||||
|
TestFailed(QString("error in included file %1: %2").arg(file).arg(msg)) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Reference in New Issue
Block a user