|
|
|
@ -117,6 +117,9 @@ class Command: public QObject { |
|
|
|
|
log(QString(" FAILED[")+demangle(typeid(e).name())+"]: "+e.what()); |
|
|
|
|
throw e; |
|
|
|
|
} |
|
|
|
|
virtual int steps(Script* parent) { |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
void line(int linenr) { |
|
|
|
|
_line = linenr; |
|
|
|
|
} |
|
|
|
@ -187,6 +190,8 @@ class Command: public QObject { |
|
|
|
|
QCoreApplication::processEvents(QEventLoop::AllEvents, 100); |
|
|
|
|
} |
|
|
|
|
protected: |
|
|
|
|
std::shared_ptr<Script> subParser(Script* parent, const QStringList& in, |
|
|
|
|
const QString& file, int line, int indent); |
|
|
|
|
bool runScript(Logger& log, Command* parentCommand, |
|
|
|
|
std::shared_ptr<Script> script, |
|
|
|
|
Script* parent, QWebFrame* frame, |
|
|
|
@ -204,7 +209,7 @@ class Command: public QObject { |
|
|
|
|
} |
|
|
|
|
QStringList quotedStrings(QString value, |
|
|
|
|
QString delimiter = " ", |
|
|
|
|
bool keepDelimiters = false) { |
|
|
|
|
bool keepDelimiters = false) const { |
|
|
|
|
QStringList res; |
|
|
|
|
QString quot("'\""); |
|
|
|
|
while (value=value.trimmed(), value.size()) { |
|
|
|
@ -222,17 +227,10 @@ class Command: public QObject { |
|
|
|
|
res += value.mid(start, m.capturedStart()-start); |
|
|
|
|
value.remove(0, m.capturedEnd()); |
|
|
|
|
if (keepDelimiters && m.capturedLength()) res+=m.captured().mid(start).trimmed(); |
|
|
|
|
// std::cout<<"REMOVE: \""<<m.captured()<<"\" 0 - "<<(m.capturedEnd()+start)
|
|
|
|
|
// <<" start="<<start<<" pos="<<pos<<std::endl
|
|
|
|
|
// <<"REMAINING: \""<<value<<"\""<<std::endl;
|
|
|
|
|
} |
|
|
|
|
// std::cout<<"FOUND"<<std::endl;
|
|
|
|
|
// Q_FOREACH(QString tag, res) {
|
|
|
|
|
// std::cout<<" - \""<<tag<<"\""<<std::endl;
|
|
|
|
|
// }
|
|
|
|
|
return res; |
|
|
|
|
} |
|
|
|
|
QStringList commaSeparatedList(QString value) { |
|
|
|
|
QStringList commaSeparatedList(QString value) const { |
|
|
|
|
return quotedStrings(value, ","); |
|
|
|
|
} |
|
|
|
|
static QWebElement find(QWebFrame* frame, QString selector, |
|
|
|
@ -487,12 +485,14 @@ class Script: public QObject { |
|
|
|
|
.replace(" ", " ").replace("&", "&"); |
|
|
|
|
} |
|
|
|
|
public: |
|
|
|
|
Script(): _clicktype(JAVASCRIPT_CLICK), _command(0), _screenshots(true), |
|
|
|
|
_defaultTimeout(20) { |
|
|
|
|
Script(): |
|
|
|
|
_step(0), _clicktype(JAVASCRIPT_CLICK), _command(0), |
|
|
|
|
_screenshots(true), _defaultTimeout(20) { |
|
|
|
|
initPrototypes(); |
|
|
|
|
} |
|
|
|
|
Script(const Script& o): |
|
|
|
|
QObject(), |
|
|
|
|
_step(0), |
|
|
|
|
_prototypes(o._prototypes), |
|
|
|
|
_script(o._script), |
|
|
|
|
_command(0), |
|
|
|
@ -568,8 +568,8 @@ class Script: public QObject { |
|
|
|
|
reset(); |
|
|
|
|
_variables.clear(); |
|
|
|
|
_rvariables.clear(); |
|
|
|
|
_functions.clear(); |
|
|
|
|
_timeout = _defaultTimeout; |
|
|
|
|
_step = 0; |
|
|
|
|
_clicktype = JAVASCRIPT_CLICK; |
|
|
|
|
} |
|
|
|
|
std::shared_ptr<Command> parseLine(QStringList& in, |
|
|
|
@ -600,6 +600,7 @@ class Script: public QObject { |
|
|
|
|
throw; |
|
|
|
|
} |
|
|
|
|
void parse(QStringList in, QString filename, int line = 1, int indent = 0) { |
|
|
|
|
_filename = filename; |
|
|
|
|
for (int linenr(0), oldsize(0); |
|
|
|
|
oldsize=in.size(), in.size(); |
|
|
|
|
linenr+=oldsize-in.size()) |
|
|
|
@ -619,6 +620,7 @@ class Script: public QObject { |
|
|
|
|
QString td = QString(), bool screenshots = true, |
|
|
|
|
int maxretries = 0) { |
|
|
|
|
bool res(true); |
|
|
|
|
_step = 0; |
|
|
|
|
_testsuites = testsuites; |
|
|
|
|
_timeout = _defaultTimeout; // defaults to 20s
|
|
|
|
|
_ignoreSignalsUntil.clear(); |
|
|
|
@ -636,10 +638,11 @@ class Script: public QObject { |
|
|
|
|
testsuite.attr("name") = "Unnamed Test Suite"; |
|
|
|
|
(*_testsuites)<<testsuite; |
|
|
|
|
} |
|
|
|
|
int retries(0), back(0), step(0); |
|
|
|
|
for (auto cmd(_script.begin()); cmd!=_script.end(); ++cmd, ++step) { |
|
|
|
|
int retries(0), back(0); |
|
|
|
|
for (auto cmd(_script.begin()); cmd!=_script.end(); |
|
|
|
|
_step+=(*cmd)->steps(this), ++cmd) { |
|
|
|
|
progress(QString("%1:%2").arg((*cmd)->file()).arg((*cmd)->line()), |
|
|
|
|
step, steps()); |
|
|
|
|
_step, countSteps()); |
|
|
|
|
xml::Node testcase("testcase"); |
|
|
|
|
try { |
|
|
|
|
testcase.attr("classname") = |
|
|
|
@ -670,8 +673,7 @@ class Script: public QObject { |
|
|
|
|
break; // test is successfully finished
|
|
|
|
|
} |
|
|
|
|
progress(QString("%1:%2").arg((*cmd)->file()).arg((*cmd)->line()), |
|
|
|
|
step, steps()); |
|
|
|
|
|
|
|
|
|
_step, countSteps()); |
|
|
|
|
} catch (PossibleRetryLoad& e) { |
|
|
|
|
_timer.stop(); |
|
|
|
|
// timeout may happen during load due to bad internet connection
|
|
|
|
@ -691,13 +693,13 @@ class Script: public QObject { |
|
|
|
|
QUrl url(frame->url()); |
|
|
|
|
if ((*cmd)->command()=="expect loadFinished true") { |
|
|
|
|
------cmd; |
|
|
|
|
------step; |
|
|
|
|
------_step; |
|
|
|
|
back += 3; |
|
|
|
|
_ignoreSignalsUntil = "loadStarted"; |
|
|
|
|
frame->load(url); |
|
|
|
|
} else if ((*cmd)->command()=="expect loadStarted") { |
|
|
|
|
----cmd; |
|
|
|
|
----step; |
|
|
|
|
----_step; |
|
|
|
|
back += 2; |
|
|
|
|
_ignoreSignalsUntil = "loadStarted"; |
|
|
|
|
frame->page()->triggerAction(QWebPage::Stop); |
|
|
|
@ -706,7 +708,7 @@ class Script: public QObject { |
|
|
|
|
url2.remove("expect urlChanged"); |
|
|
|
|
if (url2.size()) url=url2.trimmed(); |
|
|
|
|
----cmd; |
|
|
|
|
----step; |
|
|
|
|
----_step; |
|
|
|
|
back += 2; |
|
|
|
|
_ignoreSignalsUntil = "loadStarted"; |
|
|
|
|
frame->load(url); |
|
|
|
@ -715,7 +717,7 @@ class Script: public QObject { |
|
|
|
|
url2.remove("expect load"); |
|
|
|
|
if (url2.size()) url=url2.trimmed(); |
|
|
|
|
----cmd; |
|
|
|
|
----step; |
|
|
|
|
----_step; |
|
|
|
|
back += 2; |
|
|
|
|
_ignoreSignalsUntil = "loadStarted"; |
|
|
|
|
frame->load(url); |
|
|
|
@ -791,8 +793,11 @@ class Script: public QObject { |
|
|
|
|
QString& cerr() { |
|
|
|
|
return _cerr; |
|
|
|
|
} |
|
|
|
|
int steps() { |
|
|
|
|
return _script.size(); |
|
|
|
|
int countSteps() { |
|
|
|
|
int res(0); |
|
|
|
|
for (auto cmd(_script.begin()); cmd!=_script.end(); ++cmd) |
|
|
|
|
res += (*cmd)->steps(this); |
|
|
|
|
return res; |
|
|
|
|
} |
|
|
|
|
bool screenshots() { |
|
|
|
|
return _screenshots; |
|
|
|
@ -839,7 +844,7 @@ class Script: public QObject { |
|
|
|
|
QStringList variables() { |
|
|
|
|
return _variables.keys(); |
|
|
|
|
} |
|
|
|
|
QString variable(Logger& log, QString name) { |
|
|
|
|
QString variable(QString name) { |
|
|
|
|
QMap<QString, QString>::iterator it(_variables.find(name)); |
|
|
|
|
if (it==_variables.end()) error(VariableNotFound(name)); |
|
|
|
|
return *it; |
|
|
|
@ -871,7 +876,7 @@ class Script: public QObject { |
|
|
|
|
void function(QString name, std::shared_ptr<Function> f) { |
|
|
|
|
_functions[name] = f; |
|
|
|
|
} |
|
|
|
|
std::shared_ptr<Function> function(Logger& log, QString name) { |
|
|
|
|
std::shared_ptr<Function> function(QString name) { |
|
|
|
|
QMap<QString, std::shared_ptr<Function> >::iterator |
|
|
|
|
it(_functions.find(name)); |
|
|
|
|
if (it==_functions.end()) error(FunctionNotFound(name)); |
|
|
|
@ -1017,6 +1022,9 @@ class Script: public QObject { |
|
|
|
|
_prototypes[c->tag()] = std::shared_ptr<Command>(c); |
|
|
|
|
} |
|
|
|
|
private Q_SLOTS: |
|
|
|
|
void innerProgress(QString txt, int delta) { |
|
|
|
|
progress(txt, _step+delta, countSteps()); |
|
|
|
|
} |
|
|
|
|
void authenticationRequired(QNetworkReply*, QAuthenticator* a) { |
|
|
|
|
if (_auth.contains(a->realm())) { |
|
|
|
|
log("network: login to "+a->realm()); |
|
|
|
@ -1083,6 +1091,7 @@ class Script: public QObject { |
|
|
|
|
Commands _script; |
|
|
|
|
std::queue<Signal> _signals; |
|
|
|
|
QTimer _timer; |
|
|
|
|
int _step; |
|
|
|
|
QSet<QString> _ignores; |
|
|
|
|
QString _cout; |
|
|
|
|
QString _cerr; |
|
|
|
@ -1100,9 +1109,22 @@ class Script: public QObject { |
|
|
|
|
QString _testclass; |
|
|
|
|
Command* _command; |
|
|
|
|
QString _path; |
|
|
|
|
QString _filename; |
|
|
|
|
QMap<QString, AuthRealm> _auth; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
class CommandContainer: public Command { |
|
|
|
|
public: |
|
|
|
|
int steps(Script* parent) { |
|
|
|
|
return countSteps(parent); |
|
|
|
|
} |
|
|
|
|
protected: |
|
|
|
|
virtual int countSteps(Script* parent) const { |
|
|
|
|
return _script->countSteps()+1; |
|
|
|
|
} |
|
|
|
|
std::shared_ptr<Script> _script; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
class Do: public Command { |
|
|
|
|
public: |
|
|
|
|
QString tag() const { |
|
|
|
@ -2083,7 +2105,7 @@ class SetValue: public Command { |
|
|
|
|
QString _value; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
class Function: public Command { |
|
|
|
|
class Function: public CommandContainer { |
|
|
|
|
public: |
|
|
|
|
QString tag() const { |
|
|
|
|
return "function"; |
|
|
|
@ -2115,8 +2137,7 @@ class Function: public Command { |
|
|
|
|
cmd->_name = allargs.takeFirst().trimmed(); |
|
|
|
|
cmd->_vars = commaSeparatedList(allargs.join(' ')); |
|
|
|
|
script->function(cmd->_name, cmd); |
|
|
|
|
cmd->_script = std::shared_ptr<Script>(new Script); |
|
|
|
|
cmd->_script->parse(subCommandBlock(in), file, line+1, indent+1); |
|
|
|
|
cmd->_script = cmd->subParser(script, subCommandBlock(in), file, line, indent); |
|
|
|
|
return cmd; |
|
|
|
|
} |
|
|
|
|
bool execute(Script* script, QWebFrame*) { |
|
|
|
@ -2134,7 +2155,6 @@ class Function: public Command { |
|
|
|
|
private: |
|
|
|
|
QString _name; |
|
|
|
|
QStringList _vars; |
|
|
|
|
std::shared_ptr<Script> _script; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
class Call: public Command { |
|
|
|
@ -2165,15 +2185,18 @@ class Call: public Command { |
|
|
|
|
} |
|
|
|
|
bool execute(Script* script, QWebFrame* frame) { |
|
|
|
|
Logger log(this, script); |
|
|
|
|
return script->function(log, _name)->call(log, this, script->replacevars(_args), |
|
|
|
|
return script->function(_name)->call(log, this, script->replacevars(_args), |
|
|
|
|
script, frame); |
|
|
|
|
} |
|
|
|
|
int steps(Script* parent) { |
|
|
|
|
return parent->function(_name)->steps(parent); |
|
|
|
|
} |
|
|
|
|
public: |
|
|
|
|
QString _name; |
|
|
|
|
QStringList _args; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
class If: public Command { |
|
|
|
|
class If: public CommandContainer { |
|
|
|
|
public: |
|
|
|
|
QString tag() const { |
|
|
|
|
return "if"; |
|
|
|
@ -2213,7 +2236,7 @@ class If: public Command { |
|
|
|
|
return tag()+" "+_variable+" "+_cmp+" "+_value |
|
|
|
|
+(_script.get()?"\n "+_script->print().join("\n "):""); |
|
|
|
|
} |
|
|
|
|
std::shared_ptr<Command> parse(Script*, QString args, |
|
|
|
|
std::shared_ptr<Command> parse(Script* script, QString args, |
|
|
|
|
QStringList& in, QString file, int line, |
|
|
|
|
int indent) { |
|
|
|
|
std::shared_ptr<If> cmd(new If()); |
|
|
|
@ -2229,12 +2252,10 @@ class If: public Command { |
|
|
|
|
} |
|
|
|
|
cmd->_variable = args.left(pos).trimmed(); |
|
|
|
|
cmd->_value = args.mid(pos+len).trimmed(); |
|
|
|
|
cmd->_script = std::shared_ptr<Script>(new Script); |
|
|
|
|
cmd->_script->parse(subCommandBlock(in), file, line+1, indent+1); |
|
|
|
|
cmd->_script = cmd->subParser(script, subCommandBlock(in), file, line, indent); |
|
|
|
|
if (in.size() && in.first().contains(QRegularExpression("^ *else *$"))) { |
|
|
|
|
in.removeFirst(); |
|
|
|
|
cmd->_else = std::shared_ptr<Script>(new Script); |
|
|
|
|
cmd->_else->parse(subCommandBlock(in), file, line+1, indent+1); |
|
|
|
|
cmd->_else = cmd->subParser(script, subCommandBlock(in), file, line, indent); |
|
|
|
|
} |
|
|
|
|
return cmd; |
|
|
|
|
} |
|
|
|
@ -2256,35 +2277,39 @@ class If: public Command { |
|
|
|
|
+selector+" "+_cmp+" "+value); |
|
|
|
|
} else { |
|
|
|
|
switch (_cmp[0].toLatin1()) { |
|
|
|
|
case '=': check = script->variable(log, _variable)==value; |
|
|
|
|
case '=': check = script->variable(_variable)==value; |
|
|
|
|
break; |
|
|
|
|
case '!': check = script->variable(log, _variable)!=value; |
|
|
|
|
case '!': check = script->variable(_variable)!=value; |
|
|
|
|
break; |
|
|
|
|
case '.': check = script->variable(log, _variable).contains(value); |
|
|
|
|
case '.': check = script->variable(_variable).contains(value); |
|
|
|
|
break; |
|
|
|
|
case '^': check = !script->variable(log, _variable).contains(value); |
|
|
|
|
case '^': check = !script->variable(_variable).contains(value); |
|
|
|
|
break; |
|
|
|
|
case '~': check = |
|
|
|
|
script->variable(log, _variable).contains(QRegularExpression(value)); |
|
|
|
|
script->variable(_variable).contains(QRegularExpression(value)); |
|
|
|
|
break; |
|
|
|
|
case '<': check = script->variable(log, _variable).toInt()<value.toInt(); |
|
|
|
|
case '<': check = script->variable(_variable).toInt()<value.toInt(); |
|
|
|
|
break; |
|
|
|
|
case '>': check = script->variable(log, _variable).toInt()>value.toInt(); |
|
|
|
|
case '>': check = script->variable(_variable).toInt()>value.toInt(); |
|
|
|
|
break; |
|
|
|
|
default:; |
|
|
|
|
} |
|
|
|
|
log(QString("evaluated expression to ")+(check?"true":"false")+": " |
|
|
|
|
+script->variable(log, _variable)+" "+_cmp+" "+value); |
|
|
|
|
+script->variable(_variable)+" "+_cmp+" "+value); |
|
|
|
|
} |
|
|
|
|
if (check) return runScript(log, this, _script, script, frame); |
|
|
|
|
else if (_else) return runScript(log, this, _else, script, frame); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
protected: |
|
|
|
|
int countSteps(Script* parent) { |
|
|
|
|
int res1(CommandContainer::countSteps(parent)), res2(_else->countSteps()+1); |
|
|
|
|
return res1 > res2 ? res1 : res2; |
|
|
|
|
} |
|
|
|
|
private: |
|
|
|
|
QString _variable; |
|
|
|
|
QString _cmp; |
|
|
|
|
QString _value; |
|
|
|
|
std::shared_ptr<Script> _script; |
|
|
|
|
std::shared_ptr<Script> _else; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
@ -2428,7 +2453,7 @@ class Check: public Command { |
|
|
|
|
std::shared_ptr<Command> _next; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
class For: public Command { |
|
|
|
|
class For: public CommandContainer { |
|
|
|
|
public: |
|
|
|
|
QString tag() const { |
|
|
|
|
return "for"; |
|
|
|
@ -2452,29 +2477,35 @@ class For: public Command { |
|
|
|
|
QString command() const { |
|
|
|
|
return tag()+" "+_variable+" "+_vals.join(" "); |
|
|
|
|
} |
|
|
|
|
std::shared_ptr<Command> parse(Script*, QString args, |
|
|
|
|
std::shared_ptr<Command> parse(Script* script, QString args, |
|
|
|
|
QStringList& in, QString file, int line, int indent) { |
|
|
|
|
std::shared_ptr<For> cmd(new For()); |
|
|
|
|
if (!args.size()) throw BadArgument(tag()+" requires a <variable>"); |
|
|
|
|
QStringList allargs(args.split("->")); |
|
|
|
|
cmd->_variable = allargs.takeFirst().trimmed(); |
|
|
|
|
cmd->_vals = commaSeparatedList(allargs.join("->")); |
|
|
|
|
cmd->_script = std::shared_ptr<Script>(new Script); |
|
|
|
|
cmd->_script->parse(subCommandBlock(in), file, line+1, indent+1); |
|
|
|
|
cmd->_script = cmd->subParser(script, subCommandBlock(in), file, line, indent); |
|
|
|
|
return cmd; |
|
|
|
|
} |
|
|
|
|
bool execute(Script* script, QWebFrame* frame) { |
|
|
|
|
Logger log(this, script); |
|
|
|
|
Q_FOREACH(QString i, _vals.size()?_vals:commaSeparatedList(script->variable(log, _variable))) { |
|
|
|
|
Q_FOREACH(QString i, _vals.size()?_vals:commaSeparatedList(script->variable(_variable))) { |
|
|
|
|
if (!runScript(log, this, _script, script, frame, QStringList()<<_variable, QStringList()<<i)) |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
int countSteps(Script* parent) const { |
|
|
|
|
int sz(1); |
|
|
|
|
if (_vals.size()) |
|
|
|
|
sz = _vals.size(); |
|
|
|
|
else if (parent->variables().contains(_variable)) |
|
|
|
|
sz = commaSeparatedList(parent->variable(_variable)).size(); |
|
|
|
|
return (sz?sz:1)*CommandContainer::countSteps(parent); |
|
|
|
|
} |
|
|
|
|
private: |
|
|
|
|
QString _variable; |
|
|
|
|
QStringList _vals; |
|
|
|
|
std::shared_ptr<Script> _script; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
class Echo: public Command { |
|
|
|
@ -2575,7 +2606,7 @@ class ClearCookies: public Command { |
|
|
|
|
QString _url; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
class Include: public Command { |
|
|
|
|
class Include: public CommandContainer { |
|
|
|
|
public: |
|
|
|
|
QString tag() const { |
|
|
|
|
return "include"; |
|
|
|
@ -2597,11 +2628,12 @@ class Include: public Command { |
|
|
|
|
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); |
|
|
|
|
cmd->_script = cmd->subParser(script, txt.split('\n'), cmd->_filename, 0, indent); |
|
|
|
|
} catch (Exception& e) { |
|
|
|
|
throw ParseIncludeFailed(cmd->_filename, e.what()); |
|
|
|
|
} |
|
|
|
|
Q_FOREACH(QString key, cmd->_script->functions()) // copy new functions to parent
|
|
|
|
|
script->function(key, cmd->_script->function(key)); |
|
|
|
|
return cmd; |
|
|
|
|
} |
|
|
|
|
bool execute(Script* script, QWebFrame* frame) { |
|
|
|
@ -2614,7 +2646,6 @@ class Include: public Command { |
|
|
|
|
} |
|
|
|
|
private: |
|
|
|
|
QString _filename; |
|
|
|
|
std::shared_ptr<Script> _script; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
class Case: public Command { |
|
|
|
@ -2670,7 +2701,7 @@ class Case: public Command { |
|
|
|
|
} |
|
|
|
|
return tag()+" "+_variable+body; |
|
|
|
|
} |
|
|
|
|
std::shared_ptr<Command> parse(Script*, QString args, |
|
|
|
|
std::shared_ptr<Command> parse(Script* script, QString args, |
|
|
|
|
QStringList& in, QString file, int line, |
|
|
|
|
int indent) { |
|
|
|
|
std::shared_ptr<Case> cmd(new Case()); |
|
|
|
@ -2684,9 +2715,9 @@ class Case: public Command { |
|
|
|
|
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)); |
|
|
|
|
std::shared_ptr<Script> sub |
|
|
|
|
(cmd->subParser(script, subCommandBlock(body), file, line, indent+1)); |
|
|
|
|
cmd->_conditions.append(Condition(cmp, value, sub)); |
|
|
|
|
} |
|
|
|
|
return cmd; |
|
|
|
|
} |
|
|
|
@ -2712,31 +2743,39 @@ class Case: public Command { |
|
|
|
|
+selector+" "+condition.cmp+" "+value); |
|
|
|
|
} else { |
|
|
|
|
switch (condition.cmp[0].toLatin1()) { |
|
|
|
|
case '=': check = script->variable(log, _variable)==value; |
|
|
|
|
case '=': check = script->variable(_variable)==value; |
|
|
|
|
break; |
|
|
|
|
case '!': check = script->variable(log, _variable)!=value; |
|
|
|
|
case '!': check = script->variable(_variable)!=value; |
|
|
|
|
break; |
|
|
|
|
case '.': check = script->variable(log, _variable).contains(value); |
|
|
|
|
case '.': check = script->variable(_variable).contains(value); |
|
|
|
|
break; |
|
|
|
|
case '^': check = !script->variable(log, _variable).contains(value); |
|
|
|
|
case '^': check = !script->variable(_variable).contains(value); |
|
|
|
|
break; |
|
|
|
|
case '~': check = |
|
|
|
|
script->variable(log, _variable).contains(QRegularExpression(value)); |
|
|
|
|
script->variable(_variable).contains(QRegularExpression(value)); |
|
|
|
|
break; |
|
|
|
|
case '<': check = script->variable(log, _variable).toInt()<value.toInt(); |
|
|
|
|
case '<': check = script->variable(_variable).toInt()<value.toInt(); |
|
|
|
|
break; |
|
|
|
|
case '>': check = script->variable(log, _variable).toInt()>value.toInt(); |
|
|
|
|
case '>': check = script->variable(_variable).toInt()>value.toInt(); |
|
|
|
|
break; |
|
|
|
|
default:; |
|
|
|
|
} |
|
|
|
|
log(QString("evaluated expression to ")+(check?"true":"false")+": " |
|
|
|
|
+script->variable(log, _variable)+" "+condition.cmp+" "+value); |
|
|
|
|
+script->variable(_variable)+" "+condition.cmp+" "+value); |
|
|
|
|
} |
|
|
|
|
if (check) return runScript(log, this, condition.script, script, frame); |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
private: |
|
|
|
|
int countSteps(Script* parent) { |
|
|
|
|
int res1(0); |
|
|
|
|
for (auto condition: _conditions) { |
|
|
|
|
int res2(condition.script->countSteps()+1); |
|
|
|
|
if (res2>res1) res1=res2; |
|
|
|
|
} |
|
|
|
|
return res1; |
|
|
|
|
} |
|
|
|
|
struct Condition { |
|
|
|
|
Condition(QString c, QString v, std::shared_ptr<Script> s): |
|
|
|
|
cmp(c), value(v), script(s) { |
|
|
|
@ -2888,6 +2927,16 @@ inline Logger::~Logger() { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
inline std::shared_ptr<Script> Command::subParser(Script* parent, const QStringList& in, |
|
|
|
|
const QString& file, |
|
|
|
|
int line, int indent) { |
|
|
|
|
std::shared_ptr<Script> res(new Script); |
|
|
|
|
Q_FOREACH(QString key, parent->functions()) // copy functions from parent
|
|
|
|
|
res->function(key, parent->function(key)); |
|
|
|
|
res->parse(in, file, line+1, indent+1); |
|
|
|
|
return res; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
inline bool Command::runScript(Logger& log, Command* parentCommand, |
|
|
|
|
std::shared_ptr<Script> script, |
|
|
|
|
Script* parent, QWebFrame* frame, |
|
|
|
@ -2906,21 +2955,25 @@ inline bool Command::runScript(Logger& log, Command* parentCommand, |
|
|
|
|
try { |
|
|
|
|
assert(connect(&scriptCopy, SIGNAL(logging(QString)), |
|
|
|
|
parent, SLOT(parentlog(QString)))); |
|
|
|
|
assert(connect(&scriptCopy, SIGNAL(progress(QString, int, int)), |
|
|
|
|
parent, SLOT(innerProgress(QString, int)))); |
|
|
|
|
parent->removeSignals(frame); |
|
|
|
|
bool res(scriptCopy.run(frame)); |
|
|
|
|
parent->addSignals(frame); |
|
|
|
|
disconnect(&scriptCopy, SIGNAL(progress(QString, int, int)), |
|
|
|
|
parent, SLOT(innerProgress(QString, int))); |
|
|
|
|
disconnect(&scriptCopy, SIGNAL(logging(QString)), |
|
|
|
|
parent, SLOT(parentlog(QString))); |
|
|
|
|
parentCommand->_result = scriptCopy.result(); |
|
|
|
|
Q_FOREACH(QString key, scriptCopy.variables()) // copy new variables to parent
|
|
|
|
|
if (!vars.contains(key)) parent->set(key, scriptCopy.variable(log, key)); |
|
|
|
|
Q_FOREACH(QString key, scriptCopy.functions()) // copy new functions to parent
|
|
|
|
|
parent->function(key, scriptCopy.function(log, key)); |
|
|
|
|
if (!vars.contains(key)) parent->set(key, scriptCopy.variable(key)); |
|
|
|
|
if (parentCommand->_result.size()) |
|
|
|
|
parent->log("result: "+parentCommand->_result); |
|
|
|
|
return res; |
|
|
|
|
} catch (const Exception& x) { |
|
|
|
|
parent->addSignals(frame); |
|
|
|
|
disconnect(&scriptCopy, SIGNAL(progress(QString, int, int)), |
|
|
|
|
parent, SLOT(innerProgress(QString, int))); |
|
|
|
|
disconnect(&scriptCopy, SIGNAL(logging(QString)), |
|
|
|
|
parent, SLOT(parentlog(QString))); |
|
|
|
|
throw; |
|
|
|
|