From 605c4eea79497076280b9e294e67b95176b08f20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20W=C3=A4ckerlin?= Date: Fri, 28 Jul 2017 09:29:36 +0000 Subject: [PATCH] improved waiting for elements: defaults to timeout-1 if element should be there, defaults to timeout/3 if element is optional --- COPYING | 2 +- INSTALL | 2 +- ax_check_qt.m4 | 6 +- ax_init_standard_project.m4 | 3 +- bootstrap.sh | 39 ++++---- dependency-graph.sh | 171 ++++++++++++++++++++++++++++++++++++ src/commands.hxx | 55 ++++++++---- template.sh | 121 +++++++++++++++++++++++++ 8 files changed, 356 insertions(+), 43 deletions(-) create mode 100755 dependency-graph.sh create mode 100755 template.sh diff --git a/COPYING b/COPYING index 2fcb217..88798ab 120000 --- a/COPYING +++ b/COPYING @@ -1 +1 @@ -/usr/share/automake-1.13/COPYING \ No newline at end of file +/usr/share/automake-1.15/COPYING \ No newline at end of file diff --git a/INSTALL b/INSTALL index 8b641e3..ddcdb76 120000 --- a/INSTALL +++ b/INSTALL @@ -1 +1 @@ -/usr/share/automake-1.13/INSTALL \ No newline at end of file +/usr/share/automake-1.15/INSTALL \ No newline at end of file diff --git a/ax_check_qt.m4 b/ax_check_qt.m4 index ad59f21..2d88048 100644 --- a/ax_check_qt.m4 +++ b/ax_check_qt.m4 @@ -274,9 +274,9 @@ AC_DEFUN([AX_QT_NO_KEYWORDS], [ ]) AC_DEFUN([AX_INIT_QT], [ - test -f src/makefile.in && cat >> src/makefile.in <> src/makefile.in <> "${srcdir}/${3}" fi @@ -1229,4 +1229,5 @@ AC_DEFUN([AX_OUTPUT], [ AX_RPM_RESOLVE AC_OUTPUT AX_INIT_QT + AC_MSG_NOTICE([configured for ${PACKAGE_NAME}-${VERSION}]) ]) diff --git a/bootstrap.sh b/bootstrap.sh index fbc2814..ba29eaf 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -138,6 +138,8 @@ GENERATED FILES * 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 * mac-create-app-bundle.sh - script to create apple mac os-x app-bundle + * dependency-graph.sh - script to draw project dependencies + * template.sh - generic template for bash scripts * test/runtests.sh - template file to run test scripts, i.e. docker based * AUTHORS - replace your name in AUTHORS before first run * NEWS - empty file add your project's news @@ -549,6 +551,8 @@ copy rpmsign.exp copy build-resource-file.sh copy sql-to-dot.sed copy mac-create-app-bundle.sh +copy dependency-graph.sh +copy template.sh AUTHOR=$(gpg -K 2>/dev/null | sed -n 's,uid *\(\[ultimate\] *\)\?,,p' | head -1) if test -z "${AUTHOR}"; then AUTHOR="FIRSTNAME LASTNAME (URL) " @@ -1635,7 +1639,7 @@ BuildRequires: which, pkgconfig, gnupg, expect, ${VCSDEPENDS_RPM}make, automake, %if 0%{?suse_version} || 0%{?sles_version} BuildRequires: lsb-release$( if testtag AX_REQUIRE_QT || testtag AX_CHECK_QT; then - echo -n ", libqt5-qtbase-devel, libqt5-qttools, libQt5WebKit5-devel libqt5-qtwebengine-devel libQt5WebKitWidgets-devel"; + echo -n ", libqt5-qtbase-devel, libqt5-qttools, libqt5-linguist-devel, libQt5WebKit5-devel libqt5-qtwebengine-devel libQt5WebKitWidgets-devel"; fi) %else $( @@ -1695,25 +1699,20 @@ rm -rf \$RPM_BUILD_ROOT $(if testtag AX_USE_LIBTOOL; then echo '/usr/%_lib/*.so.*' else -echo '/usr/bin/*' -echo '/usr/share/applications/*' +echo '/usr/bin' +echo '/usr/share/applications' fi) -$(if testtag AX_USE_NODEJS AX_BUILD_HTML AX_BUILD_HTML_NPM; then -echo '/usr/share/@PACKAGE_NAME@' -fi) -%doc -$(if testtag AX_USE_LIBTOOL; then - cat <&3 +} + +# write error message +error() { + echo -en "\e[1;31merror" 1>&2 + append_msg $* 1>&2 +} + +# write a warning message +warning() { + echo -en "\e[1;33mwarning" 1>&2 + append_msg $* 1>&2 +} + +# write a success message +success() { + echo -en "\e[1;32msuccess" 1>&2 + append_msg $* 1>&2 +} + +# commandline parameter evaluation +files=${0%/*}/configure.ac +short=0 +while test $# -gt 0; do + case "$1" in + (--short|-s) short=1;; + (--help|-h) less < + +OPTIONS + + --help, -h show this help + --short, -s short graph with no external dependencies + + list of zero or more configure.ac files + (default: ${files}) + +DESCRIPTION + + Evaluates dependencies of all the given configure.ac file. By + default takes the local configure.ac. Outputs a graphwiz dot file + with the dependencies. Solid lines are required dependencies, dotted + lines are optional dependencies. + +EXAMPLE + + Evaluate all dependencies between all local subversion and git + projects, if they are in the path ~/svn and ~/git: + + $0 ~/svn/*/configure.ac ~/git/*/configure.ac + +EOF + exit;; + (*) files=$*; break;; + esac + if test $# -eq 0; then + error "missing parameter, try $0 --help"; exit 1 + fi + shift; +done + +# run a command, print the result and abort in case of error +# option: --no-check: ignore the result, continue in case of error +run() { + check=1 + while test $# -gt 0; do + case "$1" in + (--no-check) check=0;; + (*) break;; + esac + shift; + done + echo -en "\e[1m-> running:\e[0m $* ..." + result=$($* 2>&1) + res=$? + if test $res -ne 0; then + if test $check -eq 1; then + error "failed with return code: $res" + if test -n "$result"; then + echo "$result" + fi + exit 1 + else + warning "ignored return code: $res" + fi + else + success + fi +} + +# error handler +function traperror() { + set +x + local err=($1) # error status + local line="$2" # LINENO + local linecallfunc="$3" + local command="$4" + local funcstack="$5" + for e in ${err[@]}; do + if test -n "$e" -a "$e" != "0"; then + error "line $line - command '$command' exited with status: $e (${err[@]})" + if [ "${funcstack}" != "main" -o "$linecallfunc" != "0" ]; then + echo -n " ... error at ${funcstack} " + if [ "$linecallfunc" != "" ]; then + echo -n "called at line $linecallfunc" + fi + echo + fi + exit $e + fi + done + success + exit 0 +} + +# catch errors +trap 'traperror "$? ${PIPESTATUS[@]}" $LINENO $BASH_LINENO "$BASH_COMMAND" "${FUNCNAME[@]}" "${FUNCTION}"' ERR SIGINT INT TERM EXIT + +########################################################################################## + +filter() { + if test $short -eq 1; then + all=$(cat) + allowed=$(sed -n '/"\(.*\)" \[style=solid\];/{s//\1/;H};${x;s/\n//;s/\n/\\|/gp}' <<<"${all}") + sed -n '/"\('"${allowed}"'\)" -> "\('"${allowed}"'\)"/p' <<<"${all}" + else + cat + fi +} + +echo "digraph G {" +if test $short -eq 0; then + echo "node [style=dashed];" +fi +( + for file in $files; do + if ! test -e $file; then + error "file $file not found"; exit 1 + fi + sed -n ' + /^ *m4_define(x_package_name, */ {s//"/;s/ *).*/"/;h;s/.*/& [style=solid];/p} + /^ *AX_REQUIRE_QT/ {s/.*/"qt" -> /;G;s/\n//;s/.*/&;/p} + /^ *AX_PKG_REQUIRE(\[\?\([^],)]\+\)\]\?, \[\?\([^],)]\+\)\]\?.*/ {s//"\2" -> /;G;s/\n//;s/.*/&;/p} + /^ *AX_PKG_REQUIRE(\[\?\([^],)]\+\)\]\?.*/ {s//"\1" -> /;G;s/\n//;s/.*/&;/p} + /^ *AX_CHECK_QT/ {s/.*/"qt" -> /;G;s/\n//;s/.*/& [style=dashed];/p} + /^ *AX_PKG_CHECK(\[\?\([^],)]\+\)\]\?, \[\?\([^],)]\+\)\]\?.*/ {s//"\2" -> /;G;s/\n//;s/.*/& [style=dotted];/p} + /^ *AX_PKG_CHECK(\[\?\([^],)]\+\)\]\?.*/ {s//"\1" -> /;G;s/\n//;s/.*/& [style=dotted];/p} + ' $file + done +) | filter +echo "}" diff --git a/src/commands.hxx b/src/commands.hxx index fba1676..4a59303 100644 --- a/src/commands.hxx +++ b/src/commands.hxx @@ -33,6 +33,7 @@ #include #include #include +#include #ifdef HAVE_CXXABI_H #include @@ -151,11 +152,8 @@ class Command: public QObject { virtual bool isTestcase() { return true; } - static void realMouseClick(Logger& log, QWebFrame* frame, QString selector) { - QWebElement element(find(frame, selector)); - if (element.isNull()) error(log, ElementNotFound(selector)); - realMouseClick(element); - } + static void realMouseClick(Logger& log, QWebFrame* frame, + Script* script, QString selector); static void realMouseClick(const QWebElement& element) { QWidget* web(element.webFrame()->page()->view()); QRect elGeom=element.geometry(); @@ -248,8 +246,9 @@ class Command: public QObject { return element; } static QWebElement find1(QWebFrame* frame, QString selector, - int repeat = 5, int sleepsec = 1) { + int repeat = 2, int sleepsec = 1) { QWebElement element; + if (repeat<1) repeat=1; for (int i=0; ifindFirstElement(selector); if (!element.isNull()) return element; @@ -257,7 +256,7 @@ class Command: public QObject { element = find1(childFrame, selector, 1, 0); if (!element.isNull()) return element; } - if (sleepsec) sleep(sleepsec); + if (i+1 @@ -1023,7 +1026,7 @@ class Script: public QObject { this, SLOT(urlChanged(const QUrl&))); disconnect(frame, SIGNAL(urlChanged(const QUrl&)), this, SLOT(urlChanged(const QUrl&))); - disconnect(&_timer, SIGNAL(timeout()), this, SLOT(timeout())); + disconnect(&_timer, SIGNAL(timeout()), this, SLOT(timedout())); } public Q_SLOTS: void log(QString text, Command* command = 0) { @@ -1144,7 +1147,7 @@ class Script: public QObject { _signals.push(std::make_pair("urlChanged", QStringList(url.toString()))); } - void timeout() { + void timedout() { error(TimeOut()); } private: @@ -1222,7 +1225,7 @@ class Do: public Command { Logger log(this, script); QWebElement element(frame->documentElement()); if (_selector.size()) { - element = find(frame, script->replacevars(_selector)); + element = find(frame, script->replacevars(_selector), script->timeout()-1); if (element.isNull()) error(log, ElementNotFound(script->replacevars(_selector))); } @@ -1305,7 +1308,7 @@ class Expect: public Command { Logger log(this, script); QStringList args(script->replacevars(_signal._args)); if (_signal._signal=="load") { // special signal load - while (!script->ignore("loadStarted") && script->checkSignal("loadStarted")) { + while (!script->ignores("loadStarted") && script->checkSignal("loadStarted")) { log("ignore signal: loadStarted"); // ignore optional loadStarted } if (!script->checkSignal("urlChanged", args)) @@ -1532,7 +1535,7 @@ class Upload: public Command { if (files.size()==1) filename=files[0]; } page->setNextUploadFile(filename); - realMouseClick(log, frame, script->replacevars(_selector)); + realMouseClick(log, frame, script, script->replacevars(_selector)); if (page->uploadPrepared()) error(log, SetFileUploadFailed(script->replacevars(_selector), filename)); return true; @@ -1576,7 +1579,7 @@ class Exists: public Command { QString selector(script->replacevars(_selector)); QString text(script->replacevars(_text)); QStringList notfound; - QWebElement firstelement(find(frame, selector)); + QWebElement firstelement(find(frame, selector, script->timeout()-1)); Q_FOREACH(QWebElement element, frame->findAllElements(selector)) { if (text.isEmpty()) return true; // just find element if (element.toOuterXml().indexOf(text)!=-1) return true; @@ -1631,6 +1634,8 @@ class Not: public Command { Logger log(this, script); QString selector(script->replacevars(_selector)); QString text(script->replacevars(_text)); + QWebElement firstelement(find(frame, selector, + mrw::max(mrw::min(script->timeout()/3, 10), 2))); Q_FOREACH(QWebElement element, frame->findAllElements(selector)) { if (text.isEmpty()) error(log, AssertionFailed("element must not exists: "+selector)); @@ -1828,12 +1833,12 @@ class Click: public Command { QString clicktarget(script->replacevars(_selector)); switch (_clicktype ? *_clicktype : script->clicktype()) { case Script::REAL_MOUSE_CLICK: { - realMouseClick(log, frame, clicktarget); + realMouseClick(log, frame, script, clicktarget); break; } case Script::JAVASCRIPT_CLICK: default: { - QWebElement element(find(frame, clicktarget)); + QWebElement element(find(frame, clicktarget, script->timeout()-1)); if (element.isNull()) error(log, ElementNotFound(clicktarget)); _result = element.evaluateJavaScript("this.click();").toString(); @@ -2145,7 +2150,8 @@ class SetValue: public Command { } bool execute(Script* script, QWebFrame* frame) { Logger log(this, script); - QWebElement element(find(frame, script->replacevars(_selector))); + QWebElement element(find(frame, script->replacevars(_selector), + script->timeout()-1)); if (element.isNull()) error(log, ElementNotFound(script->replacevars(_selector))); QString value(script->replacevars(_value)); @@ -2334,6 +2340,8 @@ class If: public CommandContainer { QString selector(script->replacevars(_variable)); bool check(false); if (_cmp=="->") { + QWebElement firstelement(find(frame, selector, + mrw::max(mrw::min(script->timeout()/3, 10), 2))); Q_FOREACH(QWebElement element, frame->findAllElements(selector)) { if (value.isEmpty() || // just find element element.toOuterXml().indexOf(value)!=-1 || @@ -2439,6 +2447,8 @@ class While: public CommandContainer { for (bool check(true); check;) { if (_cmp=="->") { check = false; + QWebElement firstelement(find(frame, selector, + mrw::max(mrw::min(script->timeout()/3, 10), 2))); Q_FOREACH(QWebElement element, frame->findAllElements(selector)) { if (value.isEmpty() || // just find element element.toOuterXml().indexOf(value)!=-1 || @@ -2899,6 +2909,8 @@ class Case: public Command { log("terminate with default branch"); check = true; } else if (condition.cmp=="->") { + QWebElement firstelement(find(frame, selector, + mrw::max(mrw::min(script->timeout()/3, 10), 2))); Q_FOREACH(QWebElement element, frame->findAllElements(selector)) { if (value.isEmpty() || // just find element element.toOuterXml().indexOf(value)!=-1 || @@ -3159,6 +3171,13 @@ inline Logger::~Logger() { } } +inline void Command::realMouseClick(Logger& log, QWebFrame* frame, + Script* script, QString selector) { + QWebElement element(find(frame, selector, script->timeout()-1)); + if (element.isNull()) error(log, ElementNotFound(selector)); + realMouseClick(element); +} + inline std::shared_ptr