summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2013-02-10 19:50:07 (GMT)
committerStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2013-02-10 19:50:07 (GMT)
commit7c779099b3acd1fa969dde718299484ebe0d2775 (patch)
tree79abec37c8a92264b7d157533a21df53057757c7
parent71a3ca4fd78d7a9cca844e81f29f48b9c36bd4c7 (diff)
downloaduscxml-7c779099b3acd1fa969dde718299484ebe0d2775.zip
uscxml-7c779099b3acd1fa969dde718299484ebe0d2775.tar.gz
uscxml-7c779099b3acd1fa969dde718299484ebe0d2775.tar.bz2
Various bug fixes
-rw-r--r--src/uscxml/Interpreter.cpp297
-rw-r--r--src/uscxml/Interpreter.h5
-rw-r--r--src/uscxml/concurrency/eventqueue/DelayedEventQueue.h4
-rwxr-xr-xtest/run-scxml-test-framework.sh102
-rw-r--r--test/scxml-test-framework/test/history/history0.json6
-rw-r--r--test/scxml-test-framework/test/send-data/send1.scxml4
-rw-r--r--test/src/scxml-test-framework-client.cpp16
7 files changed, 346 insertions, 88 deletions
diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp
index 5b947a7..4f1f31f 100644
--- a/src/uscxml/Interpreter.cpp
+++ b/src/uscxml/Interpreter.cpp
@@ -303,6 +303,7 @@ void Interpreter::interpret() {
}
enterStates(initialTransitions);
+// assert(hasLegalConfiguration());
mainEventLoop();
// set datamodel to null from this thread
@@ -473,10 +474,15 @@ void Interpreter::mainEventLoop() {
if (!_internalQueue.empty())
continue;
+ // assume that we have a legal configuration as soon as the internal queue is empty
+ assert(hasLegalConfiguration());
+
monIter = _monitors.begin();
- while(monIter != _monitors.end()) {
- (*monIter)->onStableConfiguration(this);
- monIter++;
+ if (!_sendQueue || _sendQueue->isEmpty()) {
+ while(monIter != _monitors.end()) {
+ (*monIter)->onStableConfiguration(this);
+ monIter++;
+ }
}
// whenever we have a stable configuration, run the mainThread hooks with 200fps
@@ -674,7 +680,9 @@ void Interpreter::send(const Arabica::DOM::Node<std::string>& element) {
if (HAS_ATTR(element, "namelist")) {
std::vector<std::string> names = tokenizeIdRefs(ATTR(element, "namelist"));
for (int i = 0; i < names.size(); i++) {
- sendReq.namelist[names[i]] = _dataModel.evalAsString(names[i]);
+ std::string namelistValue = _dataModel.evalAsString(names[i]);
+ sendReq.namelist[names[i]] = namelistValue;
+ sendReq.compound[names[i]] = Data(namelistValue, Data::VERBATIM);
}
}
@@ -697,6 +705,7 @@ void Interpreter::send(const Arabica::DOM::Node<std::string>& element) {
std::string paramKey = ATTR(params[i], "name");
boost::algorithm::to_lower(paramKey);
sendReq.params.insert(std::make_pair(paramKey, paramValue));
+ sendReq.compound[paramKey] = Data(paramValue, Data::VERBATIM);
}
// content
@@ -706,12 +715,17 @@ void Interpreter::send(const Arabica::DOM::Node<std::string>& element) {
if (contents.size() > 0) {
if (HAS_ATTR(contents[0], "expr")) {
if (_dataModel) {
- sendReq.content = _dataModel.evalAsString(ATTR(contents[0], "expr"));
+ std::string contentValue = _dataModel.evalAsString(ATTR(contents[0], "expr"));
+ sendReq.content = contentValue;
+ sendReq.atom = contentValue;
+ sendReq.type = Data::VERBATIM;
} else {
LOG(ERROR) << "content element has expr attribute but no datamodel is specified.";
}
} else if (contents[0].hasChildNodes()) {
sendReq.content = contents[0].getFirstChild().getNodeValue();
+ sendReq.atom = sendReq.content;
+ sendReq.type = Data::VERBATIM;
} else {
LOG(ERROR) << "content element does not specify any content.";
}
@@ -743,6 +757,8 @@ void Interpreter::delayedSend(void* userdata, std::string eventName) {
} else {
LOG(ERROR) << "Can not send to parent, we were not invoked" << std::endl;
}
+ } else if (boost::iequals(sendReq.target, "#_internal")) {
+ INSTANCE->_internalQueue.push_back(sendReq);
} else if (sendReq.target.find_first_of("#_") == 0) {
// send to invoker
std::string invokeId = sendReq.target.substr(2, sendReq.target.length() - 2);
@@ -912,9 +928,13 @@ Arabica::XPath::NodeSet<std::string> Interpreter::selectTransitions(const std::s
for (unsigned int i = 0; i < atomicStates.size(); i++) {
NodeSet<std::string> ancestors = getProperAncestors(atomicStates[i], Arabica::DOM::Node<std::string>());
- ancestors.push_back(atomicStates[i]);
- for (unsigned int j = 0; j < ancestors.size(); j++) {
- NodeSet<std::string> transitions = filterChildElements(_xmlNSPrefix + "transition", ancestors[j]);
+
+ NodeSet<std::string> sortedAncestors;
+ sortedAncestors.push_back(atomicStates[i]);
+ sortedAncestors.insert(sortedAncestors.end(), ancestors.begin(), ancestors.end());
+
+ for (unsigned int j = 0; j < sortedAncestors.size(); j++) {
+ NodeSet<std::string> transitions = filterChildElements(_xmlNSPrefix + "transition", sortedAncestors[j]);
for (unsigned int k = 0; k < transitions.size(); k++) {
if (((Arabica::DOM::Element<std::string>)transitions[k]).hasAttribute("event") &&
nameMatch(((Arabica::DOM::Element<std::string>)transitions[k]).getAttribute("event"), event) &&
@@ -927,6 +947,8 @@ Arabica::XPath::NodeSet<std::string> Interpreter::selectTransitions(const std::s
LOOP:
;
}
+
+ enabledTransitions = filterPreempted(enabledTransitions);
return enabledTransitions;
}
@@ -1029,8 +1051,12 @@ Arabica::XPath::NodeSet<std::string> Interpreter::filterPreempted(const Arabica:
Arabica::DOM::Node<std::string> t = enabledTransitions[i];
for (unsigned int j = i+1; j < enabledTransitions.size(); j++) {
Arabica::DOM::Node<std::string> t2 = enabledTransitions[j];
- if (isPreemptingTransition(t2, t))
+ if (isPreemptingTransition(t2, t)) {
+#if 0
+ std::cout << "Preempting transitions: " << std::endl << t2 << std::endl << t;
+#endif
goto LOOP;
+ }
}
filteredTransitions.push_back(t);
LOOP:
@@ -1042,6 +1068,12 @@ LOOP:
bool Interpreter::isPreemptingTransition(const Arabica::DOM::Node<std::string>& t1, const Arabica::DOM::Node<std::string>& t2) {
assert(t1);
assert(t2);
+
+#if 0
+ std::cout << "Checking preemption: " << std::endl << t1 << std::endl << t2 << std::endl;
+#endif
+
+#if 1
if (t1 == t2)
return false;
if (isWithinSameChild(t1) && (!isTargetless(t2) && !isWithinSameChild(t2)))
@@ -1049,8 +1081,49 @@ bool Interpreter::isPreemptingTransition(const Arabica::DOM::Node<std::string>&
if (!isTargetless(t1) && !isWithinSameChild(t1))
return true;
return false;
+#endif
+
+#if 0
+ // isPreempted from chris nuernberger
+ if (isTargetless(t1))
+ return false;
+
+ Arabica::DOM::Node<std::string> existingRoot = getTransitionSubgraph(t1);
+ Arabica::DOM::Node<std::string> nextRoot = getTransitionSubgraph(t2);
+
+ if (existingRoot == nextRoot || isDescendant(existingRoot, nextRoot) || isDescendant(nextRoot, existingRoot))
+ return true;
+
+ return false;
+#endif
}
+/**
+ * filterPreempted approach from chris nuernberger
+ */
+Arabica::DOM::Node<std::string> Interpreter::getTransitionSubgraph(const Arabica::DOM::Node<std::string>& transition) {
+ Arabica::XPath::NodeSet<std::string> targets = getTargetStates(transition);
+ Arabica::DOM::Node<std::string> source = getSourceState(transition);
+
+ if (!targets.size() == 0)
+ return source;
+
+ if (boost::iequals(ATTR(transition, "type"), "internal") && isCompound(source)) {
+ bool allDescendants = true;
+ for (int i = 0; i < targets.size(); i++) {
+ if (!isDescendant(targets[i], source)) {
+ allDescendants = false;
+ break;
+ }
+ }
+ if (allDescendants)
+ return source;
+ }
+
+ targets.push_back(source);
+ return findLCCA(targets);
+}
+
void Interpreter::microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
#if 0
std::cout << "Transitions: ";
@@ -1119,6 +1192,10 @@ void Interpreter::executeContent(const Arabica::DOM::Node<std::string>& content)
} else if (boost::iequals(TAGNAME(content), _xmlNSPrefix + "if")) {
// --- IF / ELSEIF / ELSE --------------
Arabica::DOM::Element<std::string> ifElem = (Arabica::DOM::Element<std::string>)content;
+#if 0
+ if (HAS_ATTR(ifElem, "cond"))
+ std::cout << ATTR(ifElem, "cond") << std::endl;
+#endif
if(hasConditionMatch(ifElem)) {
// condition is true, execute all content up to an elseif, else or end
if (ifElem.hasChildNodes()) {
@@ -1126,7 +1203,7 @@ void Interpreter::executeContent(const Arabica::DOM::Node<std::string>& content)
for (unsigned int i = 0; i < childs.getLength(); i++) {
if (childs.item(i).getNodeType() != Node_base::ELEMENT_NODE)
continue;
- if (boost::iequals(TAGNAME(childs.item(i)), _xmlNSPrefix + "elsif") ||
+ if (boost::iequals(TAGNAME(childs.item(i)), _xmlNSPrefix + "elseif") ||
boost::iequals(TAGNAME(childs.item(i)), _xmlNSPrefix + "else"))
break;
executeContent(childs.item(i));
@@ -1137,6 +1214,10 @@ void Interpreter::executeContent(const Arabica::DOM::Node<std::string>& content)
if (ifElem.hasChildNodes()) {
NodeList<std::string> elseifElem = ifElem.getElementsByTagNameNS(_nsURL, "elseif");
for (unsigned int i = 0; i < elseifElem.getLength(); i++) {
+#if 0
+ if (HAS_ATTR(elseifElem.item(i), "cond"))
+ std::cout << ATTR(elseifElem.item(i), "cond") << std::endl;
+#endif
if (hasConditionMatch(elseifElem.item(i))) {
executeContent(elseifElem.item(i).getChildNodes());
goto ELSIF_ELEM_MATCH;
@@ -1282,6 +1363,14 @@ void Interpreter::exitStates(const Arabica::XPath::NodeSet<std::string>& enabled
NodeSet<std::string> statesToExit;
std::set<InterpreterMonitor*>::iterator monIter;
+#if 1
+ std::cout << "Enabled exit transitions: " << std::endl;
+ for (int i = 0; i < enabledTransitions.size(); i++) {
+ std::cout << enabledTransitions[i] << std::endl;
+ }
+ std::cout << std::endl;
+#endif
+
for (int i = 0; i < enabledTransitions.size(); i++) {
Arabica::DOM::Element<std::string> transition = ((Arabica::DOM::Element<std::string>)enabledTransitions[i]);
if (!isTargetless(transition)) {
@@ -1306,15 +1395,36 @@ void Interpreter::exitStates(const Arabica::XPath::NodeSet<std::string>& enabled
tmpStates.push_back(source);
tmpStates.insert(tmpStates.end(), tStates.begin(), tStates.end());
+#if 1
+ std::cout << "tmpStates: ";
+ for (int i = 0; i < tmpStates.size(); i++) {
+ std::cout << ATTR(tmpStates[i], "id") << ", ";
+ }
+ std::cout << std::endl;
+#endif
ancestor = findLCCA(tmpStates);
}
+#if 1
+ std::cout << "Ancestor: " << ATTR(ancestor, "id") << std::endl;;
+#endif
+
for (int j = 0; j < _configuration.size(); j++) {
if (isDescendant(_configuration[j], ancestor))
statesToExit.push_back(_configuration[j]);
}
}
}
+
+#if 1
+ std::cout << "States to exit: ";
+ for (int i = 0; i < statesToExit.size(); i++) {
+ std::cout << ATTR(statesToExit[i], "id") << ", ";
+ }
+ std::cout << std::endl;
+
+#endif
+
// remove statesToExit from _statesToInvoke
std::list<Arabica::DOM::Node<std::string> > tmp;
for (int i = 0; i < _statesToInvoke.size(); i++) {
@@ -1340,7 +1450,7 @@ void Interpreter::exitStates(const Arabica::XPath::NodeSet<std::string>& enabled
Arabica::DOM::Element<std::string> historyElem = (Arabica::DOM::Element<std::string>)histories[j];
std::string historyType = (historyElem.hasAttribute("type") ? historyElem.getAttribute("type") : "shallow");
NodeSet<std::string> historyNodes;
- for (int k = 0; k < _configuration.size(); k++) {
+ for (int k = 0; k < _configuration.size(); k++) {
if (boost::iequals(historyType, "deep")) {
if (isAtomic(_configuration[k]) && isDescendant(_configuration[k], statesToExit[i]))
historyNodes.push_back(_configuration[k]);
@@ -1350,6 +1460,15 @@ void Interpreter::exitStates(const Arabica::XPath::NodeSet<std::string>& enabled
}
}
_historyValue[historyElem.getAttribute("id")] = historyNodes;
+#if 0
+ std::cout << "History node " << ATTR(historyElem, "id") << " contains: ";
+ for (int i = 0; i < historyNodes.size(); i++) {
+ std::cout << ATTR(historyNodes[i], "id") << ", ";
+ }
+ std::cout << std::endl;
+
+#endif
+
}
}
@@ -1389,13 +1508,21 @@ void Interpreter::enterStates(const Arabica::XPath::NodeSet<std::string>& enable
NodeSet<std::string> statesForDefaultEntry;
std::set<InterpreterMonitor*>::iterator monIter;
+#if 1
+ std::cout << "Enabled enter transitions: " << std::endl;
+ for (int i = 0; i < enabledTransitions.size(); i++) {
+ std::cout << enabledTransitions[i] << std::endl;
+ }
+ std::cout << std::endl;
+#endif
+
for (int i = 0; i < enabledTransitions.size(); i++) {
Arabica::DOM::Element<std::string> transition = ((Arabica::DOM::Element<std::string>)enabledTransitions[i]);
if (!isTargetless(transition)) {
std::string transitionType = (boost::iequals(transition.getAttribute("type"), "internal") ? "internal" : "external");
NodeSet<std::string> tStates = getTargetStates(transition);
-#if 0
+#if 1
std::cout << "Target States: ";
for (int i = 0; i < tStates.size(); i++) {
std::cout << ATTR(tStates[i], "id") << ", ";
@@ -1405,7 +1532,7 @@ void Interpreter::enterStates(const Arabica::XPath::NodeSet<std::string>& enable
Arabica::DOM::Node<std::string> ancestor;
Arabica::DOM::Node<std::string> source = getSourceState(transition);
-#if 0
+#if 1
std::cout << "Source States: " << ATTR(source, "id") << std::endl;
#endif
assert(source);
@@ -1429,7 +1556,7 @@ void Interpreter::enterStates(const Arabica::XPath::NodeSet<std::string>& enable
ancestor = findLCCA(tmpStates);
}
-#if 0
+#if 1
std::cout << "Ancestor: " << ATTR(ancestor, "id") << std::endl;
#endif
@@ -1437,10 +1564,18 @@ void Interpreter::enterStates(const Arabica::XPath::NodeSet<std::string>& enable
addStatesToEnter(tStates[j], statesToEnter, statesForDefaultEntry);
}
+#if 1
+ std::cout << "States to enter: ";
+ for (int i = 0; i < statesToEnter.size(); i++) {
+ std::cout << ATTR(statesToEnter[i], "id") << ", ";
+ }
+ std::cout << std::endl;
+#endif
+
for (int j = 0; j < tStates.size(); j++) {
NodeSet<std::string> ancestors = getProperAncestors(tStates[j], ancestor);
-#if 0
+#if 1
std::cout << "Proper Ancestors of " << ATTR(tStates[j], "id") << " and " << ATTR(ancestor, "id") << ": ";
for (int i = 0; i < ancestors.size(); i++) {
std::cout << ATTR(ancestors[i], "id") << ", ";
@@ -1579,12 +1714,16 @@ void Interpreter::addStatesToEnter(const Arabica::DOM::Node<std::string>& state,
Arabica::XPath::NodeSet<std::string>& statesToEnter,
Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) {
std::string stateId = ((Arabica::DOM::Element<std::string>)state).getAttribute("id");
- if (isHistory(state)) {
+
+#if 1
+ std::cout << "Adding state to enter: " << stateId << std::endl;
+#endif
+ if (isHistory(state)) {
if (_historyValue.find(stateId) != _historyValue.end()) {
Arabica::XPath::NodeSet<std::string> historyValue = _historyValue[stateId];
-#if 0
- std::cout << "History States: ";
+#if 1
+ std::cout << "History State " << ATTR(state, "id") << ": ";
for (int i = 0; i < historyValue.size(); i++) {
std::cout << ATTR(historyValue[i], "id") << ", ";
}
@@ -1595,7 +1734,7 @@ void Interpreter::addStatesToEnter(const Arabica::DOM::Node<std::string>& state,
addStatesToEnter(historyValue[i], statesToEnter, statesForDefaultEntry);
NodeSet<std::string> ancestors = getProperAncestors(historyValue[i], state);
-#if 0
+#if 1
std::cout << "Proper Ancestors: ";
for (int i = 0; i < ancestors.size(); i++) {
std::cout << ATTR(ancestors[i], "id") << ", ";
@@ -1604,8 +1743,6 @@ void Interpreter::addStatesToEnter(const Arabica::DOM::Node<std::string>& state,
#endif
for (int j = 0; j < ancestors.size(); j++) {
- if (boost::iequals(TAGNAME(ancestors[j]), _xmlNSPrefix + "scxml")) // do not add the scxml element itself
- continue;
statesToEnter.push_back(ancestors[j]);
}
}
@@ -1615,6 +1752,12 @@ void Interpreter::addStatesToEnter(const Arabica::DOM::Node<std::string>& state,
NodeSet<std::string> targets = getTargetStates(transitions[i]);
for (int j = 0; j < targets.size(); j++) {
addStatesToEnter(targets[j], statesToEnter, statesForDefaultEntry);
+
+ // Modifications from chris nuernberger
+ NodeSet<std::string> ancestors = getProperAncestors(targets[j], state);
+ for (int k = 0; k < ancestors.size(); k++) {
+ statesToEnter.push_back(ancestors[k]);
+ }
}
}
}
@@ -1652,6 +1795,14 @@ Arabica::XPath::NodeSet<std::string> Interpreter::getChildStates(const Arabica::
return childs;
}
+/**
+ See: http://www.w3.org/TR/scxml/#LCCA
+ The Least Common Compound Ancestor is the <state> or <scxml> element s such that s is a proper ancestor
+ of all states on stateList and no descendant of s has this property. Note that there is guaranteed to be
+ such an element since the <scxml> wrapper element is a common ancestor of all states. Note also that since
+ we are speaking of proper ancestor (parent or parent of a parent, etc.) the LCCA is never a member of stateList.
+*/
+
Arabica::DOM::Node<std::string> Interpreter::findLCCA(const Arabica::XPath::NodeSet<std::string>& states) {
#if 0
std::cout << "findLCCA: ";
@@ -1662,9 +1813,11 @@ Arabica::DOM::Node<std::string> Interpreter::findLCCA(const Arabica::XPath::Node
#endif
Arabica::XPath::NodeSet<std::string> ancestors = getProperAncestors(states[0], Arabica::DOM::Node<std::string>());
- ancestors.push_back(states[0]); // state[0] may already be the ancestor - bug in W3C spec?
+// ancestors.push_back(states[0]); // state[0] may already be the ancestor - bug in W3C spec?
Arabica::DOM::Node<std::string> ancestor;
for (int i = 0; i < ancestors.size(); i++) {
+ if (!isCompound(ancestors[i]))
+ continue;
for (int j = 0; j < states.size(); j++) {
#if 0
std::cout << "Checking " << TAGNAME(states[j]) << " and " << TAGNAME(ancestors[i]) << std::endl;
@@ -1677,6 +1830,10 @@ Arabica::DOM::Node<std::string> Interpreter::findLCCA(const Arabica::XPath::Node
NEXT_ANCESTOR:
;
}
+
+ // take uppermost root as ancestor
+ if (!ancestor)
+ ancestor = _scxml;
assert(ancestor);
#if 0
std::cout << " -> " << ATTR(ancestor, "id") << " " << ancestor.getLocalName() << std::endl;
@@ -1817,7 +1974,7 @@ NodeSet<std::string> Interpreter::filterChildElements(const std::string& tagName
NodeList<std::string> childs = node.getChildNodes();
for (unsigned int i = 0; i < childs.getLength(); i++) {
if (childs.item(i).getNodeType() != Node_base::ELEMENT_NODE ||
- !boost::iequals(LOCALNAME(childs.item(i)), tagName))
+ !boost::iequals(TAGNAME(childs.item(i)), tagName))
continue;
filteredChildElems.push_back(childs.item(i));
}
@@ -1832,6 +1989,8 @@ NodeSet<std::string> Interpreter::getProperAncestors(const Arabica::DOM::Node<st
while((node = node.getParentNode())) {
if (!isState(node))
break;
+ if (boost::iequals(TAGNAME(node), _xmlNSPrefix + "scxml")) // do not return scxml root itself - this is somewhat ill-defined
+ break;
if (!boost::iequals(TAGNAME(node), _xmlNSPrefix + "parallel") && !boost::iequals(TAGNAME(node), _xmlNSPrefix + "state") && !boost::iequals(TAGNAME(node), _xmlNSPrefix + "scxml"))
break;
if (node == s2)
@@ -1885,8 +2044,8 @@ bool Interpreter::isState(const Arabica::DOM::Node<std::string>& state) {
return true;
if (boost::iequals("parallel", tagName))
return true;
- if (boost::iequals("history", tagName))
- return true;
+// if (boost::iequals("history", tagName)) // this is no state, see mail to W3C list
+// return true;
if (boost::iequals("final", tagName))
return true;
return false;
@@ -1963,7 +2122,7 @@ bool Interpreter::isCompound(const Arabica::DOM::Node<std::string>& state) {
if (!isState(state))
return false;
- if (boost::iequals(LOCALNAME(state), "parallel"))
+ if (boost::iequals(LOCALNAME(state), "parallel")) // parallel is no compound state
return false;
Arabica::DOM::NodeList<std::string> childs = state.getChildNodes();
@@ -2027,6 +2186,92 @@ void Interpreter::setCmdLineOptions(int argc, char** argv) {
}
}
+/**
+ * See: http://www.w3.org/TR/scxml/#LegalStateConfigurations
+ */
+bool Interpreter::hasLegalConfiguration() {
+
+#if 0
+ std::cout << "Checking whether {";
+ std::string seperator;
+ for (int i = 0; i < _configuration.size(); i++) {
+ std::cout << seperator << ATTR(_configuration[i], "id");
+ seperator = ", ";
+ }
+ std::cout << "} is legal" << std::endl;
+#endif
+
+ // The configuration contains exactly one child of the <scxml> element.
+ NodeSet<std::string> scxmlChilds = getChildStates(_scxml);
+ bool foundScxmlChild = false;
+ for (int i = 0; i < scxmlChilds.size(); i++) {
+ if (isMember(scxmlChilds[i], _configuration)) {
+ if (foundScxmlChild)
+ return false;
+ foundScxmlChild = true;
+ }
+ }
+ if (!foundScxmlChild)
+ return false;
+
+ // The configuration contains one or more atomic states.
+ bool foundAtomicState = false;
+ for (int i = 0; i < _configuration.size(); i++) {
+ if (isAtomic(_configuration[i])) {
+ foundAtomicState = true;
+ break;
+ }
+ }
+ if (!foundAtomicState)
+ return false;
+
+ // When the configuration contains an atomic state, it contains all of its <state> and <parallel> ancestors.
+ for (int i = 0; i < _configuration.size(); i++) {
+ if (isAtomic(_configuration[i])) {
+ Node<std::string> parent = _configuration[i];
+ while((parent = parent.getParentNode())) {
+ if (isState(parent) &&
+ (boost::iequals(LOCALNAME(parent), "state") ||
+ boost::iequals(LOCALNAME(parent), "parallel"))) {
+ if (!isMember(parent, _configuration))
+ return false;
+ }
+ }
+ }
+ }
+
+ // When the configuration contains a non-atomic <state>, it contains one and only one of the state's children
+ for (int i = 0; i < _configuration.size(); i++) {
+ if (!isAtomic(_configuration[i]) && !isParallel(_configuration[i])) {
+ bool foundChildState = false;
+ NodeSet<std::string> childs = getChildStates(_configuration[i]);
+ for (int j = 0; j < childs.size(); j++) {
+ if (isMember(childs[j], _configuration)) {
+ if (foundChildState)
+ return false;
+ foundChildState = true;
+ }
+ }
+ if (!foundChildState)
+ return false;
+ }
+ }
+
+ // If the configuration contains a <parallel> state, it contains all of its children
+ for (int i = 0; i < _configuration.size(); i++) {
+ if (isParallel(_configuration[i])) {
+ NodeSet<std::string> childs = getChildStates(_configuration[i]);
+ for (int j = 0; j < childs.size(); j++) {
+ if (!isMember(childs[j], _configuration) && !isHistory(childs[j]))
+ return false;
+ }
+ }
+ }
+
+ // everything worked out fine!
+ return true;
+}
+
void Interpreter::dump() {
if (!_document)
return;
diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h
index ab58ec3..7b30fd9 100644
--- a/src/uscxml/Interpreter.h
+++ b/src/uscxml/Interpreter.h
@@ -151,7 +151,8 @@ public:
static bool isMember(const Arabica::DOM::Node<std::string>& node, const Arabica::XPath::NodeSet<std::string>& set);
void dump();
-
+ bool hasLegalConfiguration();
+
static bool isState(const Arabica::DOM::Node<std::string>& state);
static bool isPseudoState(const Arabica::DOM::Node<std::string>& state);
static bool isTransitionTarget(const Arabica::DOM::Node<std::string>& elem);
@@ -247,6 +248,8 @@ protected:
bool isWithinSameChild(const Arabica::DOM::Node<std::string>& transition);
bool parentIsScxmlState(Arabica::DOM::Node<std::string> state);
+ Arabica::DOM::Node<std::string> getTransitionSubgraph(const Arabica::DOM::Node<std::string>& transition);
+
static std::vector<std::string> tokenizeIdRefs(const std::string& idRefs);
static boost::uuids::random_generator uuidGen;
diff --git a/src/uscxml/concurrency/eventqueue/DelayedEventQueue.h b/src/uscxml/concurrency/eventqueue/DelayedEventQueue.h
index 84ab16e..fc00ec0 100644
--- a/src/uscxml/concurrency/eventqueue/DelayedEventQueue.h
+++ b/src/uscxml/concurrency/eventqueue/DelayedEventQueue.h
@@ -37,6 +37,10 @@ public:
void stop();
static void run(void*);
+ bool isEmpty() {
+ return _callbackData.empty();
+ }
+
static void timerCallback(evutil_socket_t fd, short what, void *arg);
static void dummyCallback(evutil_socket_t fd, short what, void *arg);
diff --git a/test/run-scxml-test-framework.sh b/test/run-scxml-test-framework.sh
index 2182e79..e20afe9 100755
--- a/test/run-scxml-test-framework.sh
+++ b/test/run-scxml-test-framework.sh
@@ -27,13 +27,13 @@ TESTS=""
# TESTS="${TESTS} scxml-test-framework/test/actionSend/send4.scxml" # won't support
# TESTS="${TESTS} scxml-test-framework/test/actionSend/send5.scxml" # won't support
# TESTS="${TESTS} scxml-test-framework/test/actionSend/send6.scxml" # won't support
-# TESTS="${TESTS} scxml-test-framework/test/actionSend/send7.scxml" # won't support
+# TESTS="${TESTS} scxml-test-framework/test/actionSend/send7.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/actionSend/send8.scxml" # won't support
# TESTS="${TESTS} scxml-test-framework/test/assign-current-small-step/test0.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/assign-current-small-step/test1.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/assign-current-small-step/test2.scxml" # passed
-# TESTS="${TESTS} scxml-test-framework/test/assign-current-small-step/test3.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/assign-current-small-step/test3.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/assign-current-small-step/test4.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/assign-next-small-step/test0.scxml" # failed
@@ -43,8 +43,8 @@ TESTS=""
# TESTS="${TESTS} scxml-test-framework/test/atom3-basic-tests/m0.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/atom3-basic-tests/m1.scxml" # passed
-# TESTS="${TESTS} scxml-test-framework/test/atom3-basic-tests/m2.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/atom3-basic-tests/m3.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/atom3-basic-tests/m2.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/atom3-basic-tests/m3.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/basic/basic0.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/basic/basic1.scxml" # passed
@@ -53,80 +53,80 @@ TESTS=""
# TESTS="${TESTS} scxml-test-framework/test/cond-js/test0.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/cond-js/test1.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/cond-js/test2.scxml" # passed
-# TESTS="${TESTS} scxml-test-framework/test/cond-js/TestConditionalTransition.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/cond-js/TestConditionalTransition.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/default-initial-state/initial1.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/default-initial-state/initial2.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/default-initial-state/initial3.scxml" # passed
-# TESTS="${TESTS} scxml-test-framework/test/delayedSend/send1.scxml" # segfault
-# TESTS="${TESTS} scxml-test-framework/test/delayedSend/send2.scxml" # segfault
-# TESTS="${TESTS} scxml-test-framework/test/delayedSend/send3.scxml" # segfault
+# TESTS="${TESTS} scxml-test-framework/test/delayedSend/send1.scxml" # won't support: stable config is c not b
+# TESTS="${TESTS} scxml-test-framework/test/delayedSend/send2.scxml" # won't support: stable config is c not b
+# TESTS="${TESTS} scxml-test-framework/test/delayedSend/send3.scxml" # won't support: stable config is c not b
# TESTS="${TESTS} scxml-test-framework/test/documentOrder/documentOrder0.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/foreach/test1.scxml" # passed
-# TESTS="${TESTS} scxml-test-framework/test/hierarchy/hier0.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/hierarchy/hier1.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/hierarchy/hier2.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/hierarchy/hier0.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/hierarchy/hier1.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/hierarchy/hier2.scxml" # passed
-# TESTS="${TESTS} scxml-test-framework/test/hierarchy+documentOrder/test0.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/hierarchy+documentOrder/test1.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/hierarchy+documentOrder/test0.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/hierarchy+documentOrder/test1.scxml" # passed
-TESTS="${TESTS} scxml-test-framework/test/history/history0.scxml" # segfault
-# TESTS="${TESTS} scxml-test-framework/test/history/history1.scxml" # segfault
-# TESTS="${TESTS} scxml-test-framework/test/history/history2.scxml" # segfault
-# TESTS="${TESTS} scxml-test-framework/test/history/history3.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/history/history4.scxml" # segfault
-# TESTS="${TESTS} scxml-test-framework/test/history/history5.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/history/history6.scxml" # segfault
+# TESTS="${TESTS} scxml-test-framework/test/history/history0.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/history/history1.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/history/history2.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/history/history3.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/history/history4.scxml" # passed: removed history from "getChildStates" - see mail to w3c list
+# TESTS="${TESTS} scxml-test-framework/test/history/history5.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/history/history6.scxml" # passed
-# TESTS="${TESTS} scxml-test-framework/test/if-else/test0.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/if-else/test0.scxml" # failed: we enter state b with a === 11
-# TESTS="${TESTS} scxml-test-framework/test/in/TestInPredicate.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/in/TestInPredicate.scxml" # passed
-# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test0.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test1.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test2.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test3.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test4.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test5.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test6.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test7.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test8.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test9.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test0.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test1.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test2.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test3.scxml" # failed: entered a1, b2 instead of a2, b2
+# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test4.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test5.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test6.scxml" # failed: entered a11, b12 instead of a22, b12
+# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test7.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test8.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test9.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/multiple-events-per-transition/test1.scxml" # passed
-# TESTS="${TESTS} scxml-test-framework/test/parallel/test0.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/parallel/test1.scxml" # exception
-# TESTS="${TESTS} scxml-test-framework/test/parallel/test2.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/parallel/test3.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel/test0.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/parallel/test1.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/parallel/test2.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/parallel/test3.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test0.scxml" # failed
# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test1.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test10.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test10.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test11.scxml" # failed
# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test12.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test13.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test13.scxml" # failed: transitioning to d not {c1, c2} on t
# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test14.scxml" # failed
# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test15.scxml" # failed
# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test16.scxml" # failed
# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test17.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test18.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test18.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test19.scxml" # failed
# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test2.scxml" # failed
# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test20.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test21.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test21.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test22.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test23.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test23.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test24.scxml" # failed
# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test25.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test26.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test26.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test27.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test28.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test29.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test28.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test29.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test3.scxml" # failed
# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test30.scxml" # failed
# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test31.scxml" # failed
@@ -134,8 +134,8 @@ TESTS="${TESTS} scxml-test-framework/test/history/history0.scxml" # segfault
# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test5.scxml" # failed
# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test6.scxml" # failed
# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test7.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test8.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test9.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test8.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test9.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/script/test0.scxml" # getData not defined
# TESTS="${TESTS} scxml-test-framework/test/script/test1.scxml" # getData not defined
@@ -149,13 +149,13 @@ TESTS="${TESTS} scxml-test-framework/test/history/history0.scxml" # segfault
# TESTS="${TESTS} scxml-test-framework/test/scxml-prefix-event-name-matching/test0.scxml" # passed
# TESTS="${TESTS} scxml-test-framework/test/scxml-prefix-event-name-matching/test1.scxml" # passed
-# TESTS="${TESTS} scxml-test-framework/test/send-data/send1.scxml" # segfault
-# TESTS="${TESTS} scxml-test-framework/test/send-internal/test0.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/send-data/send1.scxml" # failed: typing issue with ===
+# TESTS="${TESTS} scxml-test-framework/test/send-internal/test0.scxml" # failed: typing issue with ===
# TESTS="${TESTS} scxml-test-framework/test/targetless-transition/test0.scxml" # passed
-# TESTS="${TESTS} scxml-test-framework/test/targetless-transition/test1.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/targetless-transition/test2.scxml" # failed
-# TESTS="${TESTS} scxml-test-framework/test/targetless-transition/test3.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/targetless-transition/test1.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/targetless-transition/test2.scxml" # passed
+TESTS="${TESTS} scxml-test-framework/test/targetless-transition/test3.scxml" # failed
#trap 'killall ${SCXML_TEST_FRAMEWORK_NAME}' 0
diff --git a/test/scxml-test-framework/test/history/history0.json b/test/scxml-test-framework/test/history/history0.json
index c6afbd6..b614a61 100644
--- a/test/scxml-test-framework/test/history/history0.json
+++ b/test/scxml-test-framework/test/history/history0.json
@@ -3,11 +3,11 @@
"events" : [
{
"event" : { "name" : "t1" },
- "nextConfiguration" : ["b", "b2"]
+ "nextConfiguration" : ["b2"]
},
{
"event" : { "name" : "t2" },
- "nextConfiguration" : ["b", "b3"]
+ "nextConfiguration" : ["b3"]
},
{
"event" : { "name" : "t3" },
@@ -15,7 +15,7 @@
},
{
"event" : { "name" : "t1" },
- "nextConfiguration" : ["b", "b3"]
+ "nextConfiguration" : ["b3"]
}
]
}
diff --git a/test/scxml-test-framework/test/send-data/send1.scxml b/test/scxml-test-framework/test/send-data/send1.scxml
index 92c9c03..d150ad5 100644
--- a/test/scxml-test-framework/test/send-data/send1.scxml
+++ b/test/scxml-test-framework/test/send-data/send1.scxml
@@ -27,7 +27,7 @@
<state id="a">
<transition target="b" event="t">
- <send delayexpr="'10'" eventexpr="'s1'" namelist="foo bar">
+ <send delayexpr="'10ms'" eventexpr="'s1'" namelist="foo bar">
<param name="bif" location="bat"/>
<param name="belt" expr="4"/>
</send>
@@ -41,7 +41,7 @@
_event.data.bif === 3 &amp;&amp;
_event.data.belt === 4">
- <send delayexpr="'10'" eventexpr="'s2'">
+ <send delayexpr="'10ms'" eventexpr="'s2'">
<content>More content.</content>
</send>
diff --git a/test/src/scxml-test-framework-client.cpp b/test/src/scxml-test-framework-client.cpp
index 1505988..841df0f 100644
--- a/test/src/scxml-test-framework-client.cpp
+++ b/test/src/scxml-test-framework-client.cpp
@@ -36,11 +36,12 @@ public:
reply.compound["sessionToken"] = uscxml::Data(interpreter->getName());
std::string seperator;
for (size_t i = 0; i < configuration.size(); i++) {
- reply.compound["nextConfiguration"].array.push_back(uscxml::Data(ATTR(configuration[i], "id"), uscxml::Data::VERBATIM));
+ if (uscxml::Interpreter::isAtomic(configuration[i]))
+ reply.compound["nextConfiguration"].array.push_back(uscxml::Data(ATTR(configuration[i], "id"), uscxml::Data::VERBATIM));
}
-// std::cout << "---- reply:" << std::endl;
-// std::cout << reply << std::endl;
+ std::cout << "---- reply:" << std::endl;
+ std::cout << reply << std::endl;
std::stringstream replyString;
replyString << reply;
@@ -72,6 +73,11 @@ public:
std::cout << std::endl;
}
virtual void beforeExitingStates(uscxml::Interpreter* interpreter, const Arabica::XPath::NodeSet<std::string>& statesToExit) {
+ std::cout << "Configuration: ";
+ for (int i = 0; i < interpreter->getConfiguration().size(); i++) {
+ std::cout << ATTR(interpreter->getConfiguration()[i], "id") << ", ";
+ }
+ std::cout << std::endl;
std::cout << "Exiting states: ";
for (int i = 0; i < statesToExit.size(); i++) {
std::cout << ATTR(statesToExit[i], "id") << ", ";
@@ -88,7 +94,7 @@ public:
virtual void httpRecvReq(struct evhttp_request *req) {
-// std::cout << "---- received:" << std::endl;
+ std::cout << "---- received:" << std::endl;
if (evhttp_request_get_command(req) != EVHTTP_REQ_POST)
return;
@@ -117,7 +123,7 @@ public:
}
uscxml::Data jsonReq = uscxml::Data::fromJSON(content);
-// std::cout << jsonReq << std::endl;
+ std::cout << jsonReq << std::endl;
// is this a load request?