#include "uscxml/config.h" #include "uscxml/Interpreter.h" #include "uscxml/interpreter/InterpreterImpl.h" #include "uscxml/debug/InterpreterIssue.h" #include "uscxml/util/DOM.h" #include "uscxml/interpreter/Logging.h" #include #include uscxml::Factory* factory = NULL; using namespace uscxml; std::set issueLocationsForXML(const std::string xml) { Interpreter interpreter = Interpreter::fromXML(xml, ""); // common xmlns and version requirement on scxml attribute interpreter.getImpl()->getDocument()->getDocumentElement()->setAttribute(X("xmlns"), X("http://www.w3.org/2005/07/scxml")); interpreter.getImpl()->getDocument()->getDocumentElement()->setAttribute(X("version"), X("1.0")); std::list issues = interpreter.validate(); std::set issueLocations; for (std::list::iterator issueIter = issues.begin(); issueIter != issues.end(); issueIter++) { std::cout << *issueIter << std::endl; issueLocations.insert(issueIter->xPath); } return issueLocations; } size_t runtimeIssues; class IssueMonitor : public InterpreterMonitor { public: IssueMonitor() { runtimeIssues = 0; } void reportIssue(const std::string& sessionId, const InterpreterIssue& issue) { runtimeIssues++; } }; bool endlessLoop() { if (Factory::getInstance()->hasDataModel("ecmascript")) { // Potential endless loop const char* xml = "" " " " " " " " 0\" />" " " " " " " ""; IssueMonitor monitor; Interpreter interpreter = Interpreter::fromXML(xml, ""); interpreter.addMonitor(&monitor); while(interpreter.step() > 0) {} // four identical configurations between macrosteps assert(runtimeIssues == 4); } return true; } bool unreachableState() { if (1) { // Unreachable states 1 const char* xml = "" " " " " " " " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"bar\"]") != issueLocations.end()); assert(issueLocations.size() == 1); } return true; } bool invalidParents() { if (1) { // Invalid parents const char* xml = "" " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("/scxml[1]/onentry[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } return true; } bool noIdAttr() { if (1) { // State has no 'id' attribute // *** This is not actually an error! *** const char* xml = "" " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("/scxml[1]/state[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } return true; } bool duplicateIdAttr() { if (1) { // Duplicate state with id const char* xml = "" " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]") != issueLocations.end()); assert(issueLocations.size() == 1); } return true; } bool invalidTarget() { if (1) { // Transition has non-existant target state const char* xml = "" " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]/transition[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } return true; } bool uselessHistory1() { if (1) { // Useless history 1 const char* xml = "" " " " " " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//history[@id=\"bar\"]") != issueLocations.end()); assert(issueLocations.size() == 1); } return true; } bool uselessHistory2() { if (1) { // Useless history 2 const char* xml = "" " " " " " " " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//history[@id=\"bar\"]") != issueLocations.end()); assert(issueLocations.size() == 1); } return true; } bool illegalCompletion() { if (1) { // No legal completion const char* xml = "" " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]") != issueLocations.end()); assert(issueLocations.find("//state[@id=\"start\"]/transition[1]") != issueLocations.end()); assert(issueLocations.size() == 2); } return true; } bool attributeConstraints() { if (1) { // attribute constraints { // initial attribute and child const char* xml = "" " " " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]") != issueLocations.end()); assert(issueLocations.size() == 1); } { // initial attribute with atomic state const char* xml = "" " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]") != issueLocations.end()); assert(issueLocations.size() == 1); } { // initial child with atomic state const char* xml = "" " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]") != issueLocations.end()); assert(issueLocations.size() == 2); // also invalid non-child target state in initial } // combinations of namelist, content and param { // send with content and namelist, not allowed const char* xml = "" " " " " " " " Foo!" " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]/onentry[1]/send[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } { // send with content and params, not allowed const char* xml = "" " " " " " " " " " Foo!" " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]/onentry[1]/send[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } { // send with params and namelist, perfectly acceptable const char* xml = "" " " " " " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.size() == 0); } { // invoke with content and src, not allowed const char* xml = "" " " " " " Foo!" " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]/invoke[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } { // source with src and child content, not allowed const char* xml = "" " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]/onentry[1]/script[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } { // invoke with namelist and param, not allowed const char* xml = "" " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]/invoke[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } { // invoke with param and content, perfectly acceptable const char* xml = "" " " " " " " " Foo!" " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.size() == 0); } { // invoke with namelist and content, perfectly acceptable const char* xml = "" " " " " " Foo!" " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.size() == 0); } { // donedata with content and param, not allowed const char* xml = "" " " " " " " " Foo!" " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]/donedata[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } } return true; } bool optimallyEnabled() { if (1) { // Transition can never be optimally enabled (conditionless, eventless) const char* xml = "" " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]/transition[2]") != issueLocations.end()); assert(issueLocations.size() == 1); } if (1) { // Transition can never be optimally enabled (conditionless, more events) const char* xml = "" " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]/transition[2]") != issueLocations.end()); assert(issueLocations.size() == 1); } return true; } bool invalidInitial() { if (1) { // Initial attribute has invalid target state const char* xml = "" ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("/scxml[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } if (1) { // Initial attribute with target outside of children const char* xml = "" " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]") != issueLocations.end()); assert(issueLocations.size() == 1); } if (1) { // Initial transition with target outside of children const char* xml = "" " " " " " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]/initial[1]/transition[1]") != issueLocations.end()); assert(issueLocations.size() == 1); // there are actually two issues with the transition } if (1) { // Initial history transition with target outside of children const char* xml = "" " " " " " " " " " " " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//history[@id=\"bar\"]/transition[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } if (1) { // Initial transition with target outside of children const char* xml = "" " " " " " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]/initial[1]/transition[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } if (1) { // Initial transition with event const char* xml = "" " " " " " " " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]/initial[1]/transition[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } if (1) { // Initial transition with condition const char* xml = "" " " " " " " " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]/initial[1]/transition[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } if (1) { // Initial with multiple transitions const char* xml = "" " " " " " " " " " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]/initial[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } if (1) { // Initial with no transitions const char* xml = "" " " " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"foo\"]") != issueLocations.end()); // unreachable assert(issueLocations.find("//state[@id=\"start\"]/initial[1]") != issueLocations.end()); assert(issueLocations.size() == 2); } return true; } bool invalidHistory() { if (1) { // History transition with event const char* xml = "" " " " " " " " " " " " " " " " " " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//history[@id=\"bar\"]/transition[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } if (1) { // History transition with condition const char* xml = "" " " " " " " " " " " " " " " " " " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//history[@id=\"bar\"]/transition[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } return true; } bool unknownIOProc() { if (1) { // Send to unknown IO Processor const char* xml = "" " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]/onentry[1]/send[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } return true; } bool unknownDataModel() { if (1) { // SCXML document requires unknown datamodel const char* xml = "" ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("/scxml[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } return true; } bool unknownExecContent() { if (1) { // Unknown executable content element const char* xml = "" " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]/onentry[1]/nonexistant[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } return true; } bool syntaxErrors() { if (Factory::getInstance()->hasDataModel("ecmascript")) { // Syntax error in script const char* xml = "" " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("/scxml[1]/script[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } if (Factory::getInstance()->hasDataModel("ecmascript")) { // Syntax error in cond attribute const char* xml = "" " " " " " " " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]/transition[1]") != issueLocations.end()); assert(issueLocations.find("//state[@id=\"start\"]/onentry[1]/if[1]") != issueLocations.end()); assert(issueLocations.find("//state[@id=\"start\"]/onentry[1]/if[1]/elseif[1]") != issueLocations.end()); assert(issueLocations.size() == 3); } if (Factory::getInstance()->hasDataModel("ecmascript")) { // Syntax error in expr attribute const char* xml = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]/onentry[1]/log[1]") != issueLocations.end()); assert(issueLocations.find("//data[@id=\"foo\"]") != issueLocations.end()); assert(issueLocations.find("//state[@id=\"start\"]/onentry[1]/assign[1]") != issueLocations.end()); assert(issueLocations.find("//state[@id=\"start\"]/onentry[1]/send[1]/param[1]") != issueLocations.end()); assert(issueLocations.find("//state[@id=\"start\"]/onentry[1]/send[2]/content[1]") != issueLocations.end()); assert(issueLocations.size() == 5); } if (Factory::getInstance()->hasDataModel("ecmascript")) { // Syntax error with foreach const char* xml = "" " " " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]/onentry[1]/foreach[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } if (Factory::getInstance()->hasDataModel("ecmascript")) { // Syntax error with send const char* xml = "" " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]/onentry[1]/send[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } if (Factory::getInstance()->hasDataModel("ecmascript")) { // Syntax error with invoke const char* xml = "" " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]/invoke[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } if (Factory::getInstance()->hasDataModel("ecmascript")) { // Syntax error with cancel const char* xml = "" " " " " " " " " " " ""; std::set issueLocations = issueLocationsForXML(xml); assert(issueLocations.find("//state[@id=\"start\"]/onentry[1]/cancel[1]") != issueLocations.end()); assert(issueLocations.size() == 1); } return true; } int main(int argc, char** argv) { factory = Factory::getInstance(); // return EXIT_SUCCESS; try { using namespace XERCESC_NS; int iterations = 1; while(iterations--) { endlessLoop(); unreachableState(); invalidParents(); noIdAttr(); duplicateIdAttr(); invalidTarget(); uselessHistory1(); uselessHistory2(); illegalCompletion(); attributeConstraints(); optimallyEnabled(); invalidInitial(); invalidHistory(); unknownIOProc(); unknownDataModel(); unknownExecContent(); syntaxErrors(); } } catch (ErrorEvent e) { std::cout << e; return EXIT_FAILURE; } return EXIT_SUCCESS; }