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