summaryrefslogtreecommitdiffstats
path: root/test/src
diff options
context:
space:
mode:
authorStefan Radomski <sradomski@mintwerk.de>2016-01-06 11:10:10 (GMT)
committerStefan Radomski <sradomski@mintwerk.de>2016-01-06 11:10:10 (GMT)
commitf9eb54fc9c17116954846133b33f7a241e662cbc (patch)
tree7b6eede00cd3c088f0f652acccc89d4471f9cfa6 /test/src
parentb8ba0e7c31f397a66f9d509ff20a85b33619475a (diff)
downloaduscxml-f9eb54fc9c17116954846133b33f7a241e662cbc.zip
uscxml-f9eb54fc9c17116954846133b33f7a241e662cbc.tar.gz
uscxml-f9eb54fc9c17116954846133b33f7a241e662cbc.tar.bz2
Prepared ChartToC transformation
Diffstat (limited to 'test/src')
-rw-r--r--test/src/test-c-machine.cpp429
-rw-r--r--test/src/test-c-machine.machine.c532
-rw-r--r--test/src/test-misc.cpp3
-rw-r--r--test/src/test-w3c.cpp68
4 files changed, 1025 insertions, 7 deletions
diff --git a/test/src/test-c-machine.cpp b/test/src/test-c-machine.cpp
new file mode 100644
index 0000000..a9e5ecb
--- /dev/null
+++ b/test/src/test-c-machine.cpp
@@ -0,0 +1,429 @@
+#include <string.h>
+#include <stdlib.h> // malloc
+#include <assert.h> // assert
+#include <stdio.h> // printf
+#include <sstream> // stringstream
+#include <deque> // deque
+#include <boost/algorithm/string.hpp> // trim
+
+#define SCXML_VERBOSE 1
+
+#ifndef AUTOINCLUDE_TEST
+#include "test-c-machine.machine.c"
+#endif
+
+#include "uscxml/Convenience.h"
+//#include "uscxml/DOMUtils.h"
+#include "uscxml/Factory.h"
+#include "uscxml/InterpreterInfo.h"
+#include "uscxml/UUID.h"
+
+#include "uscxml/concurrency/DelayedEventQueue.h"
+#include "uscxml/concurrency/tinythread.h"
+
+#define USER_DATA(ctx) ((GenCInterpreterInfo*)(((scxml_ctx*)ctx)->user_data))
+
+using namespace uscxml;
+
+typedef struct scxml_foreach_info scxml_foreach_info;
+struct scxml_foreach_info {
+ size_t iterations;
+ size_t currIteration;
+};
+
+class GenCInterpreterInfo : public InterpreterInfo {
+public:
+ NameSpaceInfo getNameSpaceInfo() const {
+ return nsInfo;
+ }
+ const std::string& getName() {
+ return name;
+ }
+ const std::string& getSessionId() {
+ return sessionId;
+ }
+ const std::map<std::string, IOProcessor>& getIOProcessors() {
+ return ioProcs;
+ }
+ bool isInState(const std::string& stateId) {
+ for (int i = 0 ; i < SCXML_NUMBER_STATES; i++) {
+ if (scxml_states[i].name != NULL && IS_SET(i, ctx->config) && stateId == scxml_states[i].name)
+ return true;
+ }
+ return false;
+ }
+ Arabica::DOM::Document<std::string> getDocument() const {
+ return document;
+ }
+ const std::map<std::string, Invoker>& getInvokers() {
+ return invokers;
+ }
+
+ NameSpaceInfo nsInfo;
+ std::string name;
+ std::string sessionId;
+ std::map<std::string, IOProcessor> ioProcs;
+ std::map<std::string, Invoker> invokers;
+ Arabica::DOM::Document<std::string> document;
+ scxml_ctx* ctx;
+ boost::shared_ptr<DataModelImpl> datamodel;
+
+ std::map<const scxml_elem_foreach*, scxml_foreach_info*> foreachInfo;
+ std::deque<Event*> iq;
+ std::deque<Event*> eq;
+
+ DelayedEventQueue delayQueue;
+ std::map<std::string, SendRequest*> sendIds;
+
+ tthread::condition_variable monitor;
+ tthread::mutex mutex;
+};
+
+int matches(const char* desc, const char* event) {
+ const char* dPtr = desc;
+ const char* ePtr = event;
+ while(true) {
+
+ // next event descriptor
+ if (*dPtr == ' ') {
+ dPtr++;
+ ePtr = event;
+ continue;
+ }
+
+ // descriptor is done, return match
+ if (*dPtr == 0 || *dPtr == '*')
+ return true;
+
+ // descriptor differs from event name
+ if (*dPtr != *ePtr)
+ return false;
+
+ // move both pointers one character
+ dPtr++;
+ ePtr++;
+ }
+}
+
+int exec_content_raise(const scxml_ctx* ctx, const char* event) {
+ Event* e = new Event();
+ e->name = strdup(event);
+ printf("Raising Internal Event: %s\n", e->name.c_str());
+ USER_DATA(ctx)->iq.push_back(e);
+ return SCXML_ERR_OK;
+}
+
+int is_true(const scxml_ctx* ctx, const char* expr) {
+ try {
+ return USER_DATA(ctx)->datamodel->evalAsBool(expr);
+ } catch (Event e) {
+ exec_content_raise(ctx, e.name.c_str());
+ }
+ return false;
+}
+
+int is_enabled(const scxml_ctx* ctx, const scxml_transition* t, const void* e) {
+ Event* event = (Event*)e;
+ if (event == NULL) {
+ if (t->event == NULL) {
+ // spontaneous transition, null event
+ if (t->condition != NULL)
+ return is_true(ctx, t->condition);
+ return true;
+ } else {
+ // spontaneous transition, but real event
+ return false;
+ }
+ }
+
+ // real transition, real event
+ if (matches(t->event, event->name.c_str())) {
+ if (t->condition != NULL)
+ return is_true(ctx, t->condition);
+ return true;
+ }
+ return false;
+}
+
+int raise_done_event(const scxml_ctx* ctx, const scxml_state* state) {
+ std::string doneName = "done.state.";
+ exec_content_raise(ctx, (doneName + state->name).c_str());
+ return SCXML_ERR_OK;
+}
+
+void delayedSend(void* ctx, std::string eventName) {
+ tthread::lock_guard<tthread::mutex> lock(USER_DATA(ctx)->mutex);
+
+ SendRequest* e = USER_DATA(ctx)->sendIds[eventName];
+ if (e->target == "#_internal") {
+ printf("Pushing Internal Event: %s\n", e->name.c_str());
+ USER_DATA(ctx)->iq.push_back(e);
+ } else {
+ printf("Pushing External Event: %s\n", e->name.c_str());
+ USER_DATA(ctx)->eq.push_back(e);
+ }
+ USER_DATA(ctx)->monitor.notify_all();
+}
+
+int exec_content_cancel(const scxml_ctx* ctx, const char* sendid, const char* sendidexpr) {
+ std::string eventId;
+ if (sendid != NULL) {
+ eventId = sendid;
+ } else if (sendidexpr != NULL) {
+ eventId = USER_DATA(ctx)->datamodel->evalAsString(sendidexpr);
+ }
+
+ if (eventId.length() > 0) {
+ USER_DATA(ctx)->delayQueue.cancelEvent(eventId);
+ } else {
+ exec_content_raise(ctx, "error.execution");
+ return SCXML_ERR_EXEC_CONTENT;
+ }
+ return SCXML_ERR_OK;
+}
+
+int exec_content_send(const scxml_ctx* ctx, const scxml_elem_send* send) {
+ SendRequest* e = new SendRequest();
+
+ std::string target;
+ if (send->target != NULL) {
+ e->target = send->target;
+ } else if (send->targetexpr != NULL) {
+ e->target = USER_DATA(ctx)->datamodel->evalAsString(send->targetexpr);
+ }
+
+ if (e->target.size() > 0 && (e->target[0] != '#' || e->target[1] != '_')) {
+ delete e;
+ exec_content_raise(ctx, "error.execution");
+ return SCXML_ERR_INVALID_TARGET;
+ }
+
+ if (send->type != NULL) {
+ e->type = send->type;
+ } else if (send->typeexpr != NULL) {
+ e->type = USER_DATA(ctx)->datamodel->evalAsString(send->typeexpr);
+ } else {
+ e->type = "http://www.w3.org/TR/scxml/#SCXMLEventProcessor";
+ }
+
+ // only one somewhat supported
+ if (e->type != "http://www.w3.org/TR/scxml/#SCXMLEventProcessor") {
+ delete e;
+ exec_content_raise(ctx, "error.execution");
+ return SCXML_ERR_INVALID_TARGET;
+ }
+
+ e->origintype = e->type;
+
+ if (send->eventexpr != NULL) {
+ e->name = USER_DATA(ctx)->datamodel->evalAsString(send->eventexpr);
+ } else {
+ e->name = strdup(send->event);
+ }
+
+ const scxml_elem_param* param = send->params;
+ while (param && ELEM_PARAM_IS_SET(param)) {
+ Data paramValue;
+ if (param->expr != NULL) {
+ paramValue = USER_DATA(ctx)->datamodel->getStringAsData(param->expr);
+ } else if(param->location) {
+ paramValue = USER_DATA(ctx)->datamodel->getStringAsData(param->location);
+ }
+ e->params.insert(std::make_pair(param->name, paramValue));
+ param++;
+ }
+
+ if (send->content != NULL) {
+ e->data = Data(send->content, Data::VERBATIM);
+ }
+
+ const char* sendid = NULL;
+ if (send->id != NULL) {
+ sendid = send->id;
+ } else {
+ sendid = strdup(UUID::getUUID().c_str());
+ if (send->idlocation != NULL)
+ USER_DATA(ctx)->datamodel->assign(send->idlocation, Data(sendid, Data::VERBATIM));
+ }
+
+ size_t delayMs = 0;
+ std::string delay;
+ if (send->delayexpr != NULL) {
+ delay = USER_DATA(ctx)->datamodel->evalAsString(send->delayexpr);
+ } else if (send->delay != NULL) {
+ delay = send->delay;
+ }
+ if (delay.size() > 0) {
+ boost::trim(delay);
+
+ NumAttr delayAttr(delay);
+ if (iequals(delayAttr.unit, "ms")) {
+ delayMs = strTo<uint32_t>(delayAttr.value);
+ } else if (iequals(delayAttr.unit, "s")) {
+ delayMs = strTo<double>(delayAttr.value) * 1000;
+ } else if (delayAttr.unit.length() == 0) { // unit less delay is interpreted as milliseconds
+ delayMs = strTo<uint32_t>(delayAttr.value);
+ } else {
+ std::cerr << "Cannot make sense of delay value " << delay << ": does not end in 's' or 'ms'";
+ }
+ }
+
+ USER_DATA(ctx)->sendIds[sendid] = e;
+ if (delayMs > 0) {
+ USER_DATA(ctx)->delayQueue.addEvent(sendid, delayedSend, delayMs, (void*)ctx);
+ } else {
+ delayedSend((void*)ctx, sendid);
+ }
+
+ return SCXML_ERR_OK;
+}
+
+int exec_content_init(const scxml_ctx* ctx, const scxml_elem_data* data) {
+ while(ELEM_DATA_IS_SET(data)) {
+ Data d;
+ if (data->expr != NULL) {
+ d = Data(data->expr, Data::INTERPRETED);
+ } else if (data->content != NULL) {
+ d = Data(data->content, Data::INTERPRETED);
+ } else {
+ d = Data("undefined", Data::INTERPRETED);
+ }
+ try {
+ USER_DATA(ctx)->datamodel->init(data->id, d);
+ } catch (Event e) {
+ exec_content_raise(ctx, e.name.c_str());
+ }
+ data++;
+ }
+ return SCXML_ERR_OK;
+}
+
+int exec_content_assign(const scxml_ctx* ctx, const char* location, const char* expr) {
+ try {
+ Data d(expr, Data::INTERPRETED);
+ USER_DATA(ctx)->datamodel->assign(location, d);
+ } catch (Event e) {
+ exec_content_raise(ctx, e.name.c_str());
+ return SCXML_ERR_EXEC_CONTENT;
+ }
+ return SCXML_ERR_OK;
+}
+
+int exec_content_foreach_init(const scxml_ctx* ctx, const scxml_elem_foreach* foreach) {
+ try {
+ scxml_foreach_info* feInfo = (scxml_foreach_info*)malloc(sizeof(scxml_foreach_info));
+ USER_DATA(ctx)->foreachInfo[foreach] = feInfo;
+
+ feInfo->iterations = USER_DATA(ctx)->datamodel->getLength(foreach->array);
+ feInfo->currIteration = 0;
+ } catch (Event e) {
+ exec_content_raise(ctx, e.name.c_str());
+ return SCXML_ERR_EXEC_CONTENT;
+ }
+ return SCXML_ERR_OK;
+}
+
+int exec_content_foreach_next(const scxml_ctx* ctx, const scxml_elem_foreach* foreach) {
+ try {
+ scxml_foreach_info* feInfo = USER_DATA(ctx)->foreachInfo[foreach];
+ if (feInfo->currIteration < feInfo->iterations) {
+ USER_DATA(ctx)->datamodel->setForeach((foreach->item != NULL ? foreach->item : ""),
+ (foreach->array != NULL ? foreach->array : ""),
+ (foreach->index != NULL ? foreach->index : ""),
+ feInfo->currIteration);
+ feInfo->currIteration++;
+ return SCXML_ERR_OK;
+ }
+ } catch (Event e) {
+ exec_content_raise(ctx, e.name.c_str());
+ free(USER_DATA(ctx)->foreachInfo[foreach]);
+ USER_DATA(ctx)->foreachInfo.erase(foreach);
+ return SCXML_ERR_EXEC_CONTENT;
+ }
+ return SCXML_ERR_FOREACH_DONE;
+}
+
+int exec_content_foreach_done(const scxml_ctx* ctx, const scxml_elem_foreach* foreach) {
+ free(USER_DATA(ctx)->foreachInfo[foreach]);
+ USER_DATA(ctx)->foreachInfo.erase(foreach);
+ return SCXML_ERR_OK;
+}
+
+int exec_content_log(const scxml_ctx* ctx, const char* label, const char* expr) {
+ if (label != 0) {
+ printf("%s: %s\n", label, expr);
+ } else {
+ printf("%s\n", USER_DATA(ctx)->datamodel->evalAsString(expr).c_str());
+ }
+ return SCXML_ERR_OK;
+}
+
+void* dequeue_external(const scxml_ctx* ctx) {
+ tthread::lock_guard<tthread::mutex> lock(USER_DATA(ctx)->mutex);
+ while (USER_DATA(ctx)->eq.size() == 0) {
+ USER_DATA(ctx)->monitor.wait(USER_DATA(ctx)->mutex);
+ }
+ Event* e = USER_DATA(ctx)->eq.front();
+ USER_DATA(ctx)->eq.pop_front();
+ USER_DATA(ctx)->datamodel->setEvent(*e);
+ printf("Popping External Event: %s\n", e->name.c_str());
+ return e;
+}
+
+void* dequeue_internal(const scxml_ctx* ctx) {
+ if (USER_DATA(ctx)->iq.size() == 0)
+ return NULL;
+ Event* e = USER_DATA(ctx)->iq.front();
+ USER_DATA(ctx)->iq.pop_front();
+ USER_DATA(ctx)->datamodel->setEvent(*e);
+ printf("Popping Internal Event: %s\n", e->name.c_str());
+ return e;
+}
+
+int main(int argc, char** argv) {
+ int err;
+
+ // setup info object required for datamodel
+ GenCInterpreterInfo interpreterInfo;
+ interpreterInfo.name = "adsf";
+ interpreterInfo.sessionId = "rfwef";
+ interpreterInfo.datamodel = Factory::getInstance()->createDataModel("ecmascript", &interpreterInfo);
+ interpreterInfo.delayQueue.start();
+
+ scxml_ctx ctx;
+ memset(&ctx, 0, sizeof(scxml_ctx));
+ interpreterInfo.ctx = &ctx;
+
+ // set info object as user data
+ ctx.user_data = (void*)&interpreterInfo;
+
+ // register callbacks with scxml context
+ ctx.is_enabled = &is_enabled;
+ ctx.is_true = &is_true;
+ ctx.raise_done_event = &raise_done_event;
+
+ ctx.exec_content_send = &exec_content_send;
+ ctx.exec_content_raise = &exec_content_raise;
+ ctx.exec_content_cancel = &exec_content_cancel;
+ ctx.exec_content_log = &exec_content_log;
+ ctx.exec_content_assign = &exec_content_assign;
+ ctx.exec_content_foreach_init = &exec_content_foreach_init;
+ ctx.exec_content_foreach_next = &exec_content_foreach_next;
+ ctx.exec_content_foreach_done = &exec_content_foreach_done;
+ ctx.dequeue_external = &dequeue_external;
+ ctx.dequeue_internal = &dequeue_internal;
+ ctx.exec_content_init = &exec_content_init;
+
+ while((err = scxml_step(&ctx)) == SCXML_ERR_OK);
+ assert(ctx.flags & SCXML_CTX_TOP_LEVEL_FINAL);
+
+ size_t passIdx = 0;
+ for (int i = 0; i < SCXML_NUMBER_STATES; i++) {
+ if (scxml_states[i].name && strcmp(scxml_states[i].name, "pass") == 0) {
+ passIdx = i;
+ break;
+ }
+ }
+ assert(IS_SET(passIdx, ctx.config));
+ return EXIT_SUCCESS;
+} \ No newline at end of file
diff --git a/test/src/test-c-machine.machine.c b/test/src/test-c-machine.machine.c
new file mode 100644
index 0000000..d4c924d
--- /dev/null
+++ b/test/src/test-c-machine.machine.c
@@ -0,0 +1,532 @@
+#include <stdint.h> // explicit types
+#include <stddef.h> // NULL
+
+#define IS_SET(idx, bitset) ((bitset[idx >> 3] & (1 << (idx & 7))) != 0)
+#define SET_BIT(idx, bitset) bitset[idx >> 3] |= (1 << (idx & 7));
+#define CLEARBIT(idx, bitset) bitset[idx >> 3] &= (1 << (idx & 7)) ^ 0xFF;
+
+// error return codes
+#define SCXML_ERR_OK 0
+#define SCXML_ERR_IDLE 1
+#define SCXML_ERR_DONE 2
+#define SCXML_ERR_MISSING_CALLBACK 3
+#define SCXML_ERR_FOREACH_DONE 4
+#define SCXML_ERR_EXEC_CONTENT 5
+#define SCXML_ERR_INVALID_TARGET 6
+#define SCXML_ERR_INVALID_TYPE 7
+
+#define SCXML_NUMBER_STATES 5
+#define SCXML_NUMBER_TRANSITIONS 4
+
+#define SCXML_TRANS_SPONTANEOUS 0x01
+#define SCXML_TRANS_TARGETLESS 0x02
+#define SCXML_TRANS_INTERNAL 0x04
+
+#define SCXML_STATE_ATOMIC 0x01
+#define SCXML_STATE_PARALLEL 0x02
+#define SCXML_STATE_COMPOUND 0x03
+#define SCXML_STATE_FINAL 0x04
+#define SCXML_STATE_HISTORY_DEEP 0x05
+#define SCXML_STATE_HISTORY_SHALLOW 0x06
+#define SCXML_STATE_INITIAL 0x07
+
+#define SCXML_CTX_PRISTINE 0x00
+#define SCXML_CTX_SPONTANEOUS 0x01
+#define SCXML_CTX_INITIALIZED 0x02
+#define SCXML_CTX_TOP_LEVEL_FINAL 0x04
+#define SCXML_CTX_TRANSITION_FOUND 0x08
+
+#define ELEM_DATA_IS_SET(data) (data->id != NULL)
+#define ELEM_PARAM_IS_SET(param) (param->name != NULL)
+
+
+typedef struct scxml_transition scxml_transition;
+typedef struct scxml_state scxml_state;
+typedef struct scxml_ctx scxml_ctx;
+typedef struct scxml_invoke scxml_invoke;
+
+typedef struct scxml_elem_send scxml_elem_send;
+typedef struct scxml_elem_param scxml_elem_param;
+typedef struct scxml_elem_data scxml_elem_data;
+typedef struct scxml_elem_foreach scxml_elem_foreach;
+
+typedef void* (*dequeue_internal_cb_t)(const scxml_ctx* ctx);
+typedef void* (*dequeue_external_cb_t)(const scxml_ctx* ctx);
+typedef int (*is_enabled_cb_t)(const scxml_ctx* ctx, const scxml_transition* transition, const void* event);
+typedef int (*is_true_cb_t)(const scxml_ctx* ctx, const char* expr);
+typedef int (*exec_content_t)(const scxml_ctx* ctx, const scxml_state* state, const void* event);
+typedef int (*raise_done_event_t)(const scxml_ctx* ctx, const scxml_state* state);
+typedef int (*invoke_t)(const scxml_ctx* ctx, const scxml_state* s, const scxml_invoke* x);
+
+typedef int (*exec_content_log_t)(const scxml_ctx* ctx, const char* label, const char* expr);
+typedef int (*exec_content_raise_t)(const scxml_ctx* ctx, const char* event);
+typedef int (*exec_content_send_t)(const scxml_ctx* ctx, const scxml_elem_send* send);
+typedef int (*exec_content_foreach_init_t)(const scxml_ctx* ctx, const scxml_elem_foreach* foreach);
+typedef int (*exec_content_foreach_next_t)(const scxml_ctx* ctx, const scxml_elem_foreach* foreach);
+typedef int (*exec_content_foreach_done_t)(const scxml_ctx* ctx, const scxml_elem_foreach* foreach);
+typedef int (*exec_content_assign_t)(const scxml_ctx* ctx, const char* location, const char* expr);
+typedef int (*exec_content_init_t)(const scxml_ctx* ctx, const scxml_elem_data* data);
+typedef int (*exec_content_cancel_t)(const scxml_ctx* ctx, const char* sendid, const char* sendidexpr);
+typedef int (*exec_content_finalize_t)(const scxml_ctx* ctx, const scxml_invoke* invoker, const void* event);
+typedef int (*exec_content_script_t)(const scxml_ctx* ctx, const char* src, const char* content);
+
+struct scxml_elem_data {
+ const char* id;
+ const char* src;
+ const char* expr;
+ const char* content;
+};
+
+struct scxml_state {
+ const char* name; // eventual name
+ exec_content_t on_entry; // on entry handlers
+ exec_content_t on_exit; // on exit handlers
+ invoke_t invoke; // invocations
+ char children[1]; // all children
+ char completion[1]; // default completion
+ char ancestors[1]; // all ancestors
+ const scxml_elem_data* data;
+ uint8_t type; // atomic, parallel, compound, final, history
+};
+
+struct scxml_transition {
+ uint16_t source;
+ char target[1];
+ const char* event;
+ const char* condition;
+ exec_content_t on_transition;
+ uint8_t type;
+ char conflicts[1];
+ char exit_set[1];
+};
+
+struct scxml_elem_foreach {
+ const char* array;
+ const char* item;
+ const char* index;
+};
+
+struct scxml_elem_param {
+ const char* name;
+ const char* expr;
+ const char* location;
+};
+
+struct scxml_elem_invoke {
+ const char* type;
+ const char* typeexpr;
+ const char* src;
+ const char* srcexpr;
+ const char* id;
+ const char* idlocation;
+ const char* namelist;
+ uint8_t autoforward;
+ const scxml_elem_param* params;
+ const exec_content_finalize_t* finalize;
+ const char* content;
+ void* user_data;
+};
+
+struct scxml_elem_send {
+ const char* event;
+ const char* eventexpr;
+ const char* target;
+ const char* targetexpr;
+ const char* type;
+ const char* typeexpr;
+ const char* id;
+ const char* idlocation;
+ const char* delay;
+ const char* delayexpr;
+ const char* namelist;
+ const char* content;
+ const scxml_elem_param* params;
+ void* user_data;
+};
+
+struct scxml_ctx {
+ uint8_t flags;
+
+ char config[1];
+ char history[1];
+ char pending_invokes[1];
+ char initialized_data[1];
+
+ void* user_data;
+
+ dequeue_internal_cb_t dequeue_internal;
+ dequeue_external_cb_t dequeue_external;
+ is_enabled_cb_t is_enabled;
+ is_true_cb_t is_true;
+ raise_done_event_t raise_done_event;
+
+ exec_content_log_t exec_content_log;
+ exec_content_raise_t exec_content_raise;
+ exec_content_send_t exec_content_send;
+ exec_content_foreach_init_t exec_content_foreach_init;
+ exec_content_foreach_next_t exec_content_foreach_next;
+ exec_content_foreach_done_t exec_content_foreach_done;
+ exec_content_assign_t exec_content_assign;
+ exec_content_init_t exec_content_init;
+ exec_content_cancel_t exec_content_cancel;
+ exec_content_script_t exec_content_script;
+ invoke_t invoke;
+};
+
+scxml_elem_data scxml_elem_datas[4] = {
+ { "Var1", NULL, NULL, NULL },
+ { NULL, NULL, NULL, NULL },
+ { "Var2", NULL, "1", NULL },
+ { NULL, NULL, NULL, NULL }
+};
+
+int s1_on_entry_0(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+ int err = SCXML_ERR_OK;
+ if (ctx->exec_content_assign != NULL) {
+ if ((ctx->exec_content_assign(ctx, "Var1", "Var2")) != SCXML_ERR_OK) return err;
+ } else {
+ return SCXML_ERR_MISSING_CALLBACK;
+ }
+ return SCXML_ERR_OK;
+}
+
+int s1_on_entry(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+ s1_on_entry_0(ctx, state, event);
+ return SCXML_ERR_OK;
+}
+
+int pass_on_entry_0(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+ int err = SCXML_ERR_OK;
+ if (ctx->exec_content_log != NULL) {
+ if ((ctx->exec_content_log(ctx, "Outcome", "'pass'")) != SCXML_ERR_OK) return err;
+ } else {
+ return SCXML_ERR_MISSING_CALLBACK;
+ }
+ return SCXML_ERR_OK;
+}
+
+int pass_on_entry(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+ pass_on_entry_0(ctx, state, event);
+ return SCXML_ERR_OK;
+}
+
+int fail_on_entry_0(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+ int err = SCXML_ERR_OK;
+ if (ctx->exec_content_log != NULL) {
+ if ((ctx->exec_content_log(ctx, "Outcome", "'fail'")) != SCXML_ERR_OK) return err;
+ } else {
+ return SCXML_ERR_MISSING_CALLBACK;
+ }
+ return SCXML_ERR_OK;
+}
+
+int fail_on_entry(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+ fail_on_entry_0(ctx, state, event);
+ return SCXML_ERR_OK;
+}
+
+scxml_state scxml_states[5] = {
+ { NULL, NULL, NULL, NULL, { 0x1e /* 01111, 1 2 3 4 */ }, { 0x02 /* 01000, 1 */ }, { 0x00 /* 00000, */ }, (const scxml_elem_data*)&scxml_elem_datas[0], SCXML_STATE_COMPOUND },
+ { "s0", NULL, NULL, NULL, { 0x00 /* 00000, */ }, { 0x00 /* 00000, */ }, { 0x01 /* 10000, 0 */ }, NULL, SCXML_STATE_ATOMIC },
+ { "s1", s1_on_entry, NULL, NULL, { 0x00 /* 00000, */ }, { 0x00 /* 00000, */ }, { 0x01 /* 10000, 0 */ }, (const scxml_elem_data*)&scxml_elem_datas[1], SCXML_STATE_ATOMIC },
+ { "pass", pass_on_entry, NULL, NULL, { 0x00 /* 00000, */ }, { 0x00 /* 00000, */ }, { 0x01 /* 10000, 0 */ }, NULL, SCXML_STATE_FINAL },
+ { "fail", fail_on_entry, NULL, NULL, { 0x00 /* 00000, */ }, { 0x00 /* 00000, */ }, { 0x01 /* 10000, 0 */ }, NULL, SCXML_STATE_FINAL }
+};
+
+scxml_transition scxml_transitions[4] = {
+ { 1, { 0x04 /* 00100 */ }, NULL, "typeof Var2 === 'undefined' ", NULL, SCXML_TRANS_SPONTANEOUS, { 0x0f /* 1111 */ }, { 0x1e /* 01111 */ } },
+ { 1, { 0x10 /* 00001 */ }, NULL, NULL, NULL, SCXML_TRANS_SPONTANEOUS, { 0x0f /* 1111 */ }, { 0x1e /* 01111 */ } },
+ { 2, { 0x08 /* 00010 */ }, NULL, "Var1===Var2", NULL, SCXML_TRANS_SPONTANEOUS, { 0x0f /* 1111 */ }, { 0x1e /* 01111 */ } },
+ { 2, { 0x10 /* 00001 */ }, NULL, NULL, NULL, SCXML_TRANS_SPONTANEOUS, { 0x0f /* 1111 */ }, { 0x1e /* 01111 */ } }
+};
+
+#ifdef SCXML_VERBOSE
+void printStateNames(const char* a) {
+ const char* seperator = "";
+ for (int i = 0; i < SCXML_NUMBER_STATES; i++) {
+ if (IS_SET(i, a)) {
+ printf("%s%s", seperator, (scxml_states[i].name != NULL ? scxml_states[i].name : "UNK"));
+ seperator = ", ";
+ }
+ }
+ printf("\n");
+}
+
+void printBitsetIndices(const char* a, size_t length) {
+ const char* seperator = "";
+ for (int i = 0; i < length; i++) {
+ if (IS_SET(i, a)) {
+ printf("%s%d", seperator, i);
+ seperator = ", ";
+ }
+ }
+ printf("\n");
+}
+#endif
+
+void bit_or(char* dest, const char* mask, size_t length) {
+ for (int i = 0; i < length; ++i) {
+ dest[i] |= mask[i];
+ }
+}
+
+void bit_copy(char* dest, const char* source, size_t length) {
+ for (int i = 0; i < length; ++i) {
+ dest[i] = source[i];
+ }
+}
+
+int bit_has_and(const char* a, const char* b, size_t length) {
+ for (int i = 0; i < length; ++i) {
+ if (a[i] & b[i])
+ return true;
+ }
+ return false;
+}
+
+void bit_and_not(char* dest, const char* mask, size_t length) {
+ for (int i = 0; i < length; ++i) {
+ dest[i] &= ~mask[i];
+ }
+}
+
+int bit_any_set(const char* a, size_t length) {
+ for (int i = 0; i < length; ++i) {
+ if (a[i] > 0)
+ return true;
+ }
+ return false;
+}
+
+int scxml_step(scxml_ctx* ctx) {
+
+#ifdef SCXML_VERBOSE
+ printStateNames(ctx->config);
+#endif
+
+MACRO_STEP:
+ ctx->flags &= ~SCXML_CTX_TRANSITION_FOUND;
+
+ if (ctx->flags & SCXML_CTX_TOP_LEVEL_FINAL)
+ return SCXML_ERR_DONE;
+
+ int err = SCXML_ERR_OK;
+ char conflicts[1] = {0};
+ char target_set[1] = {0};
+ char exit_set[1] = {0};
+ char trans_set[1] = {0};
+ char entry_set[1] = {0};
+
+ void* event;
+ if (ctx->flags == SCXML_CTX_PRISTINE) {
+ bit_or(target_set, scxml_states[0].completion, 1);
+ ctx->flags |= SCXML_CTX_SPONTANEOUS | SCXML_CTX_INITIALIZED;
+ goto COMPLETE_CONFIG;
+ }
+
+ if (ctx->flags & SCXML_CTX_SPONTANEOUS) {
+ event = NULL;
+ goto SELECT_TRANSITIONS;
+ }
+ if ((event = ctx->dequeue_internal(ctx)) != NULL) {
+ goto SELECT_TRANSITIONS;
+ }
+ if ((event = ctx->dequeue_external(ctx)) != NULL) {
+ goto SELECT_TRANSITIONS;
+ }
+
+SELECT_TRANSITIONS:
+ for (int i = 0; i < SCXML_NUMBER_TRANSITIONS; i++) {
+ // is the transition active?
+ if (IS_SET(scxml_transitions[i].source, ctx->config)) {
+ // is it non-conflicting?
+ if (!IS_SET(i, conflicts)) {
+ // is it enabled?
+ if (ctx->is_enabled(ctx, &scxml_transitions[i], event) > 0) {
+ // remember that we found a transition
+ ctx->flags |= SCXML_CTX_TRANSITION_FOUND;
+
+ // transitions that are pre-empted
+ bit_or(conflicts, scxml_transitions[i].conflicts, 1);
+
+ // states that are directly targeted (resolve as entry-set later)
+ bit_or(target_set, scxml_transitions[i].target, 1);
+
+ // states that will be left
+ bit_or(exit_set, scxml_transitions[i].exit_set, 1);
+
+ SET_BIT(i, trans_set);
+ }
+ }
+ }
+ }
+
+ if (ctx->flags & SCXML_CTX_TRANSITION_FOUND) {
+ ctx->flags |= SCXML_CTX_SPONTANEOUS;
+ } else {
+ ctx->flags &= ~SCXML_CTX_SPONTANEOUS;
+ goto MACRO_STEP;
+ }
+
+REMEMBER_HISTORY:
+ // are my ancestors in the exit set?
+ for (int i = 0; i < SCXML_NUMBER_STATES; i++) {
+ if (IS_SET(i, ctx->config) && bit_has_and(exit_set, scxml_states[i].ancestors, 1)) {
+ SET_BIT(i, ctx->history);
+ }
+ }
+
+#ifdef SCXML_VERBOSE
+ printf("Exiting: ");
+ printStateNames(exit_set);
+#endif
+
+EXIT_STATES:
+ for (int i = SCXML_NUMBER_STATES - 1; i >= 0; i--) {
+ if (IS_SET(i, exit_set) && IS_SET(i, ctx->config)) {
+ // call all on exit handlers
+ if (scxml_states[i].on_exit != NULL) {
+ if((err = scxml_states[i].on_exit(ctx, &scxml_states[i], event)) != SCXML_ERR_OK)
+ return err;
+ }
+ CLEARBIT(i, ctx->config);
+ }
+ }
+
+COMPLETE_CONFIG:
+ // calculate new entry set
+ bit_copy(entry_set, target_set, 1);
+
+ // iterate for ancestors
+ for (int i = 0; i < SCXML_NUMBER_STATES; i++) {
+ if (IS_SET(i, entry_set)) {
+ bit_or(entry_set, scxml_states[i].ancestors, 1);
+ }
+ }
+
+ADD_DESCENDANTS:
+ // iterate for descendants
+ for (int i = 0; i < SCXML_NUMBER_STATES; i++) {
+ if (IS_SET(i, entry_set)) {
+ switch (scxml_states[i].type) {
+ case SCXML_STATE_PARALLEL: {
+ bit_or(entry_set, scxml_states[i].completion, 1);
+ break;
+ }
+ case SCXML_STATE_INITIAL: {
+ for (int j = 0; j < SCXML_NUMBER_TRANSITIONS; j++) {
+ if (scxml_transitions[j].source == i) {
+ SET_BIT(j, trans_set);
+ CLEARBIT(i, entry_set);
+ bit_or(entry_set, scxml_transitions[j].target, 1);
+ // one target may have been above, reestablish completion
+ goto ADD_DESCENDANTS;
+ }
+ }
+ break;
+ }
+ case SCXML_STATE_COMPOUND: { // we need to check whether one child is already in entry_set
+ if (!bit_has_and(entry_set, scxml_states[i].children, 1)) {
+ bit_or(entry_set, scxml_states[i].completion, 1);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+#ifdef SCXML_VERBOSE
+ printf("Transitions: ");
+ printBitsetIndices(trans_set, sizeof(char) * 8 * 1);
+#endif
+
+TAKE_TRANSITIONS:
+ for (int i = 0; i < SCXML_NUMBER_TRANSITIONS; i++) {
+ if (IS_SET(i, trans_set)) {
+ // call executable content in transition
+ if (scxml_transitions[i].on_transition != NULL) {
+ if((err = scxml_transitions[i].on_transition(ctx,
+ &scxml_states[scxml_transitions[i].source],
+ event)) != SCXML_ERR_OK)
+ return err;
+ }
+ }
+ }
+
+#ifdef SCXML_VERBOSE
+ printf("Entering: ");
+ printStateNames(entry_set);
+#endif
+
+ENTER_STATES:
+ for (int i = 0; i < SCXML_NUMBER_STATES; i++) {
+ if (IS_SET(i, entry_set) && !IS_SET(i, ctx->config)) {
+ SET_BIT(i, ctx->config);
+ if (scxml_states[i].on_entry != NULL) {
+ if((err = scxml_states[i].on_entry(ctx, &scxml_states[i], event)) != SCXML_ERR_OK)
+ return err;
+ }
+
+ // initialize data
+ if(!IS_SET(i, ctx->initialized_data)) {
+ if (scxml_states[i].data != NULL && ctx->exec_content_init != NULL) {
+ ctx->exec_content_init(ctx, scxml_states[i].data);
+ }
+ SET_BIT(i, ctx->initialized_data);
+ }
+
+ // handle final states
+ if (scxml_states[i].type == SCXML_STATE_FINAL) {
+ if (scxml_states[i].ancestors[0] == 0x01) {
+ ctx->flags |= SCXML_CTX_TOP_LEVEL_FINAL;
+ } else {
+ // raise done event
+ size_t parent = 0;
+ for (int j = 0; j < SCXML_NUMBER_STATES; j++) {
+ // we could trade runtime for memory here by saving the parent index
+ if (!IS_SET(j, scxml_states[i].ancestors)) {
+ if (parent != 0) {
+ break;
+ }
+ continue;
+ } else {
+ parent = j;
+ }
+ }
+ ctx->raise_done_event(ctx, &scxml_states[parent]);
+ }
+
+ /**
+ * are we the last final state to leave a parallel state?:
+ * 1. Gather all parallel states in our ancestor chain
+ * 2. Find all states for which these parallels are ancestors
+ * 3. Iterate all active final states and remove their ancestors
+ * 4. If a state remains, not all children of a parallel are final
+ */
+ for (int j = 0; j < SCXML_NUMBER_STATES; j++) {
+ if (scxml_states[j].type == SCXML_STATE_PARALLEL) {
+ char parallel_children[2] = {0, 0};
+ size_t parallel = j;
+ for (int k = 0; k < SCXML_NUMBER_STATES; k++) {
+ if (IS_SET(parallel, scxml_states[k].ancestors) && IS_SET(k, ctx->config)) {
+ if (scxml_states[k].type == SCXML_STATE_FINAL) {
+ bit_and_not(parallel_children, scxml_states[k].ancestors, 2);
+ } else {
+ SET_BIT(k, parallel_children);
+ }
+ }
+ }
+ if (!bit_any_set(parallel_children, 2)) {
+ ctx->raise_done_event(ctx, &scxml_states[parallel]);
+ }
+ }
+ }
+
+ }
+
+ }
+ }
+
+ return SCXML_ERR_OK;
+}
+
diff --git a/test/src/test-misc.cpp b/test/src/test-misc.cpp
new file mode 100644
index 0000000..09cda31
--- /dev/null
+++ b/test/src/test-misc.cpp
@@ -0,0 +1,3 @@
+int main(int argc, char** argv) {
+ return 0;
+} \ No newline at end of file
diff --git a/test/src/test-w3c.cpp b/test/src/test-w3c.cpp
index 1480ecb..0069d0d 100644
--- a/test/src/test-w3c.cpp
+++ b/test/src/test-w3c.cpp
@@ -20,11 +20,36 @@
static bool withFlattening = false;
static double delayFactor = 1;
+static size_t benchmarkRuns = 0;
static std::string documentURI;
int retCode = EXIT_FAILURE;
uscxml::Interpreter interpreter;
+void printUsageAndExit(const char* progName) {
+ // remove path from program name
+ std::string progStr(progName);
+ if (progStr.find_last_of(PATH_SEPERATOR) != std::string::npos) {
+ progStr = progStr.substr(progStr.find_last_of(PATH_SEPERATOR) + 1, progStr.length() - (progStr.find_last_of(PATH_SEPERATOR) + 1));
+ }
+
+ printf("%s version " USCXML_VERSION " (" CMAKE_BUILD_TYPE " build - " CMAKE_COMPILER_STRING ")\n", progStr.c_str());
+ printf("Usage\n");
+ printf("\t%s", progStr.c_str());
+ printf(" [-f] [-dN] [-bN]");
+#ifdef BUILD_AS_PLUGINS
+ printf(" [-p pluginPath]");
+#endif
+ printf(" URL");
+ printf("\n");
+ printf("Options\n");
+ printf("\t-f : flatten to SCXML state-machine\n");
+ printf("\t-d FACTOR : delay factor\n");
+ printf("\t-b ITERATIONS : benchmark with number of runs\n");
+ printf("\n");
+ exit(1);
+}
+
class W3CStatusMonitor : public uscxml::StateTransitionMonitor {
void beforeCompletion(uscxml::Interpreter tmp) {
@@ -61,7 +86,7 @@ int main(int argc, char** argv) {
}
int option;
- while ((option = getopt(argc, argv, "fd:")) != -1) {
+ while ((option = getopt(argc, argv, "fd:b:")) != -1) {
switch(option) {
case 'f':
withFlattening = true;
@@ -69,6 +94,9 @@ int main(int argc, char** argv) {
case 'd':
delayFactor = strTo<double>(optarg);
break;
+ case 'b':
+ benchmarkRuns = strTo<size_t>(optarg);
+ break;
default:
break;
}
@@ -76,7 +104,7 @@ int main(int argc, char** argv) {
documentURI = argv[optind];
- LOG(INFO) << "Processing " << documentURI << (withFlattening ? " FSM converted" : "") << (delayFactor ? "" : " with delays *= " + toStr(delayFactor));
+ LOG(INFO) << "Processing " << documentURI << (withFlattening ? " FSM converted" : "") << (delayFactor ? "" : " with delays *= " + toStr(delayFactor)) << (benchmarkRuns > 0 ? " for " + toStr(benchmarkRuns) + " benchmarks" : "");
if (withFlattening) {
interpreter = Interpreter::fromURL(documentURI);
Transformer flattener = ChartToFlatSCXML::transform(interpreter);
@@ -117,11 +145,37 @@ int main(int argc, char** argv) {
}
if (interpreter) {
- W3CStatusMonitor* vm = new W3CStatusMonitor();
- interpreter.addMonitor(vm);
-
- interpreter.start();
- while(interpreter.runOnMainThread(25));
+ if (benchmarkRuns > 0) {
+ LOG(INFO) << "Benchmarking " << documentURI << (withFlattening ? " FSM converted" : "") << (delayFactor ? "" : " with delays *= " + toStr(delayFactor));
+
+ InterpreterState state = interpreter.getState();
+
+ double avg = 0;
+ uint64_t now = 0;
+ size_t remainingRuns = benchmarkRuns;
+ uint64_t start = tthread::chrono::system_clock::now();
+
+ while(remainingRuns-- > 0) {
+ now = tthread::chrono::system_clock::now();
+ for(;;) {
+ state = interpreter.step(true);
+ if (state < 0)
+ break;
+ }
+ avg += (double)(tthread::chrono::system_clock::now() - now) / (double)benchmarkRuns;
+ interpreter.reset();
+ }
+ uint64_t totalDuration = tthread::chrono::system_clock::now() - start;
+ std::cout << benchmarkRuns << " iterations in " << totalDuration << " ms" << std::endl;
+ std::cout << avg << " ms on average" << std::endl;
+
+ } else {
+ W3CStatusMonitor* vm = new W3CStatusMonitor();
+ interpreter.addMonitor(vm);
+
+ interpreter.start();
+ while(interpreter.runOnMainThread(25));
+ }
}
} catch(Event e) {
std::cout << e << std::endl;