summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt11
-rw-r--r--apps/samples/http2im/http2im.scxml3
-rw-r--r--config.h.in2
-rw-r--r--contrib/cmake/FindFFMPEG.cmake10
-rw-r--r--contrib/cmake/FindPHP5.cmake4
-rw-r--r--contrib/cmake/FindSWI.cmake8
-rw-r--r--contrib/cmake/HeaderExists.cmake1
-rw-r--r--contrib/ctest/CTestCustom.ctest.in5
-rw-r--r--contrib/src/evws/evws.c3
-rw-r--r--docs/BUILDING.md32
-rw-r--r--src/uscxml/Convenience.h27
-rw-r--r--src/uscxml/Factory.cpp17
-rw-r--r--src/uscxml/Interpreter.cpp28
-rw-r--r--src/uscxml/Message.h17
-rw-r--r--src/uscxml/URL.cpp1
-rw-r--r--src/uscxml/plugins/invoker/CMakeLists.txt20
-rw-r--r--src/uscxml/plugins/invoker/expect/ExpectInvoker.cpp359
-rw-r--r--src/uscxml/plugins/invoker/expect/ExpectInvoker.h79
-rw-r--r--src/uscxml/plugins/invoker/im/IMInvoker.cpp18
-rw-r--r--src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp40
-rw-r--r--src/uscxml/server/HTTPServer.cpp14
-rw-r--r--test/CMakeLists.txt55
-rw-r--r--test/samples/uscxml/test-expect.scxml64
-rw-r--r--test/src/test-expect.cpp58
-rw-r--r--test/src/test-url.cpp28
25 files changed, 846 insertions, 58 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4b05df3..789b231 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -672,7 +672,7 @@ endif()
set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_SHARED})
find_package(FFMPEG)
if (FFMPEG_FOUND)
- include_directories (${FFMPEG_INCLUDE_DIR})
+ include_directories (${FFMPEG_INCLUDE_DIRS})
list (APPEND USCXML_OPT_LIBS ${FFMPEG_LIBRARIES})
# required with static ffmpeg builds
# set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-Bsymbolic")
@@ -687,6 +687,15 @@ if (LIBICAL_FOUND)
endif()
set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_ORIG})
+find_package(EXPECT)
+find_package(TCL)
+if (EXPECT_FOUND AND TCL_FOUND)
+ include_directories (${EXPECT_INCLUDE_DIR})
+ include_directories (${TCL_INCLUDE_PATH})
+ list (APPEND USCXML_OPT_LIBS ${EXPECT_LIBRARY})
+ list (APPEND USCXML_OPT_LIBS ${TCL_LIBRARY})
+endif()
+
set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_SHARED})
find_package(LibPurple)
if (LIBPURPLE_FOUND)
diff --git a/apps/samples/http2im/http2im.scxml b/apps/samples/http2im/http2im.scxml
index a2b2090..369c6a6 100644
--- a/apps/samples/http2im/http2im.scxml
+++ b/apps/samples/http2im/http2im.scxml
@@ -23,7 +23,7 @@
dump(_event);</script>
<respond status="200" to="_event.origin" />
<send target="#_im" event="im.send">
- <param name="receiver" expr="'sradomski@diogenes.local'" />
+ <param name="receiver" expr="'benedikt@diogenes.local'" />
<content expr="_event.data.query.msg" />
</send>
</transition>
@@ -40,7 +40,6 @@
<!-- <script>print("\n\n"); dump(_invokers['im']);</script> -->
<send target="#_im" event="im.send">
<param name="receiver" expr="'sradomski@diogenes.local'" />
- <param name="data" expr="someBinaryData" /> <!-- Unsupported :( -->
<content><![CDATA[Have a look <a href="http://www.heise.de">here</a>]]></content>
</send>
</transition>
diff --git a/config.h.in b/config.h.in
index 666a005..b10be40 100644
--- a/config.h.in
+++ b/config.h.in
@@ -69,6 +69,8 @@
#cmakedefine OPENSSL_FOUND
#cmakedefine OPENSSL_HAS_ELIPTIC_CURVES
#cmakedefine EVENT_SSL_FOUND
+#cmakedefine EXPECT_FOUND
+#cmakedefine TCL_FOUND
/** Header files we found */
#cmakedefine HAS_UNISTD_H
diff --git a/contrib/cmake/FindFFMPEG.cmake b/contrib/cmake/FindFFMPEG.cmake
index 5f64790..261b283 100644
--- a/contrib/cmake/FindFFMPEG.cmake
+++ b/contrib/cmake/FindFFMPEG.cmake
@@ -160,5 +160,13 @@ IF (FFMPEG_LIBAVFORMAT_FOUND AND FFMPEG_LIBAVDEVICE_FOUND AND FFMPEG_LIBAVCODE
ENDIF()
INCLUDE(FindPackageHandleStandardArgs)
-FIND_PACKAGE_HANDLE_STANDARD_ARGS(FFMPEG DEFAULT_MSG FFMPEG_LIBRARIES FFMPEG_INCLUDE_DIRS)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(FFMPEG DEFAULT_MSG
+ FFMPEG_LIBRARIES
+ FFMPEG_INCLUDE_DIRS
+ FFMPEG_LIBAVFORMAT_INCLUDE_DIRS
+ FFMPEG_LIBAVDEVICE_INCLUDE_DIRS
+ FFMPEG_LIBAVCODEC_INCLUDE_DIRS
+ FFMPEG_LIBAVUTIL_INCLUDE_DIRS
+ FFMPEG_LIBSWSCALE_INCLUDE_DIRS
+)
MARK_AS_ADVANCED(FFMPEG_LIBRARIES FFMPEG_INCLUDE_DIR)
diff --git a/contrib/cmake/FindPHP5.cmake b/contrib/cmake/FindPHP5.cmake
index d449112..eee8e3f 100644
--- a/contrib/cmake/FindPHP5.cmake
+++ b/contrib/cmake/FindPHP5.cmake
@@ -1,4 +1,8 @@
if (UNIX)
+ if (APPLE)
+ # mavericks broke it - will look into it eventually
+ return()
+ endif()
if (NOT PHP_CONFIG)
find_program(PHP_CONFIG
NAMES php-config
diff --git a/contrib/cmake/FindSWI.cmake b/contrib/cmake/FindSWI.cmake
index 1eb98cd..0953307 100644
--- a/contrib/cmake/FindSWI.cmake
+++ b/contrib/cmake/FindSWI.cmake
@@ -21,8 +21,12 @@ if (SWI_FOUND)
# message("SWI_CFLAGS_OTHER_STATIC: ${SWI_CFLAGS_OTHER_STATIC}")
# message(FATAL_ERROR "")
- set(SWI_INCLUDE_DIR ${SWI_LIBRARY_DIRS})
- set(SWI_LIBRARY ${SWI_LIBRARIES})
+ set(SWI_INCLUDE_DIR ${SWI_INCLUDE_DIRS})
+
+ FIND_LIBRARY(SWI_LIBRARY
+ NAMES libswipl swipl
+ PATHS ${SWI_LIBRARY_DIRS}
+ )
FIND_PROGRAM(SWI_BINARY swipl)
diff --git a/contrib/cmake/HeaderExists.cmake b/contrib/cmake/HeaderExists.cmake
index 9d0570e..810ac65 100644
--- a/contrib/cmake/HeaderExists.cmake
+++ b/contrib/cmake/HeaderExists.cmake
@@ -4,4 +4,5 @@ CHECK_INCLUDE_FILE(string.h HAS_STRING_H)
CHECK_INCLUDE_FILE(signal.h HAS_SIGNAL_H)
CHECK_INCLUDE_FILE(execinfo.h HAS_EXECINFO_H)
CHECK_INCLUDE_FILE(dlfcn.h HAS_DLFCN_H)
+CHECK_INCLUDE_FILE(stdint.h HAS_STDINT_H)
diff --git a/contrib/ctest/CTestCustom.ctest.in b/contrib/ctest/CTestCustom.ctest.in
index e263147..6abfa28 100644
--- a/contrib/ctest/CTestCustom.ctest.in
+++ b/contrib/ctest/CTestCustom.ctest.in
@@ -11,11 +11,6 @@
set(CTEST_CUSTOM_TESTS_IGNORE
"ecma/test178.scxml"
"ecma/test230.scxml"
- "ecma/test250.scxml"
- "ecma/test307.scxml"
- "ecma/test313.scxml"
- "ecma/test314.scxml"
- "ecma/test415.scxml"
"ecma/test463.scxml"
"ecma/test464.scxml"
"ecma/test465.scxml"
diff --git a/contrib/src/evws/evws.c b/contrib/src/evws/evws.c
index 5dbd8e9..8932b9f 100644
--- a/contrib/src/evws/evws.c
+++ b/contrib/src/evws/evws.c
@@ -181,6 +181,9 @@ int evws_parse_first_line(struct evws_connection *conn, char *line) {
if (line != NULL)
return (-1);
+ (void)method;
+ (void)version;
+
if ((conn->uri = strdup(uri)) == NULL) {
return (-1);
}
diff --git a/docs/BUILDING.md b/docs/BUILDING.md
index a9096eb..9cd61eb 100644
--- a/docs/BUILDING.md
+++ b/docs/BUILDING.md
@@ -168,7 +168,37 @@ This would be all distributions based on Redhat, e.g. Fedora.
$ sudo yum install git cmake cmake-gui gcc-c++
# uscxml required dependencies
- $ sudo yum install xml2-devel
+ $ sudo yum install xml2-devel libcurl-devel
+
+#### Fedora 20
+
+Here is a complete walk-through to get uscxml running on Fedora 20, starting with the net installer.
+
+ # get us git and the developer tools
+ $ sudo yum install git gcc-c++ cmake
+
+ # uscxml required dependencies
+ $ sudo yum install libxml2-devel libcurl-devel
+
+This is sufficient to get uscxml to build. If you want some more functionality, install some more libraries:
+
+ # SWI prolog datamodel
+ $ sudo yum install pl-devel
+
+ # OpenAL invoker
+ $ sudo yum install openal-soft-devel libsndfile-devel
+
+ # scenegraph and osgconvert invoker
+ $ sudo yum install OpenSceneGraph-devel mesa-libGL-devel
+
+ # ffmpeg invoker (add repository from http://rpmfusion.org)
+ $ sudo yum install ffmpeg-devel ffmpeg-compat-devel
+
+ # calendar invoker
+ $ sudo yum install libical-devel
+
+ # expect invoker
+ $ sudo yum install expect-devel
### Console / Make
diff --git a/src/uscxml/Convenience.h b/src/uscxml/Convenience.h
index 0177476..cfaf617 100644
--- a/src/uscxml/Convenience.h
+++ b/src/uscxml/Convenience.h
@@ -20,6 +20,7 @@
#ifndef CONVENIENCE_H_LU7GZ6CB
#define CONVENIENCE_H_LU7GZ6CB
+#include <inttypes.h>
#include <boost/detail/endian.hpp>
namespace uscxml {
@@ -69,6 +70,32 @@ inline bool equals(const std::string& a, const std::string& b) {
return true;
}
+inline std::string unescape(const std::string& a) {
+ std::stringstream b;
+ // see http://en.cppreference.com/w/cpp/language/escape
+
+ std::string::const_iterator it = a.begin();
+ while (it != a.end()) {
+ char c = *it++;
+ if (c == '\\' && it != a.end()) {
+ switch (*it++) {
+ case '\\': c = '\\'; break;
+ case '0': c = '\0'; break;
+ case 'a': c = '\a'; break;
+ case 'b': c = '\b'; break;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+ case 'v': c = '\v'; break;
+ }
+ }
+ b << c;
+ }
+
+ return b.str();
+}
+
// see http://www.cplusplus.com/forum/general/27544/
// Little-endian operating systems:
diff --git a/src/uscxml/Factory.cpp b/src/uscxml/Factory.cpp
index 7677fd4..c32c6a8 100644
--- a/src/uscxml/Factory.cpp
+++ b/src/uscxml/Factory.cpp
@@ -72,6 +72,10 @@
# include "uscxml/plugins/invoker/im/IMInvoker.h"
# endif
+# if (defined EXPECT_FOUND && defined TCL_FOUND)
+# include "uscxml/plugins/invoker/expect/ExpectInvoker.h"
+# endif
+
#ifdef OPENAL_FOUND
# include "uscxml/plugins/invoker/audio/OpenALInvoker.h"
#endif
@@ -202,6 +206,13 @@ Factory::Factory() {
}
#endif
+#if (defined EXPECT_FOUND && defined TCL_FOUND)
+ {
+ ExpectInvoker* invoker = new ExpectInvoker();
+ registerInvoker(invoker);
+ }
+#endif
+
#if (defined OPENAL_FOUND && (defined LIBSNDFILE_FOUND || defined AUDIOTOOLBOX_FOUND))
{
OpenALInvoker* invoker = new OpenALInvoker();
@@ -510,7 +521,11 @@ size_t DataModelImpl::replaceExpressions(std::string& content) {
// } else {
// ss << data.atom;
// }
- ss << Data::toJSON(data);
+ if (data.atom.length() > 0) {
+ ss << data.atom;
+ } else {
+ ss << Data::toJSON(data);
+ }
replacements++;
} catch (Event e) {
// insert unsubstituted
diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp
index 8542bba..db24dc6 100644
--- a/src/uscxml/Interpreter.cpp
+++ b/src/uscxml/Interpreter.cpp
@@ -423,7 +423,15 @@ void InterpreterImpl::start() {
}
void InterpreterImpl::run(void* instance) {
- ((InterpreterImpl*)instance)->interpret();
+ try {
+ ((InterpreterImpl*)instance)->interpret();
+ } catch (Event e) {
+ LOG(ERROR) << e;
+ } catch(boost::bad_lexical_cast e) {
+ LOG(ERROR) << "InterpreterImpl::run catched exception: " << e.what();
+ } catch (...) {
+ LOG(ERROR) << "InterpreterImpl::run catched unknown exception";
+ }
}
bool InterpreterImpl::runOnMainThread(int fps, bool blocking) {
@@ -1124,8 +1132,10 @@ void InterpreterImpl::invoke(const Arabica::DOM::Node<std::string>& element) {
LOG(INFO) << "Added invoker " << invokeReq.type << " at " << invokeReq.invokeid;
try {
invoker.invoke(invokeReq);
+ } catch(boost::bad_lexical_cast e) {
+ LOG(ERROR) << "Exception caught while sending invoke request to invoker " << invokeReq.invokeid << ": " << e.what();
} catch(...) {
- LOG(ERROR) << "Exception caught while sending invoke requst to invoker " << invokeReq.invokeid;
+ LOG(ERROR) << "Unknown exception caught while sending invoke request to invoker " << invokeReq.invokeid;
}
if (_dataModel) {
try {
@@ -1641,7 +1651,13 @@ Arabica::DOM::Node<std::string> InterpreterImpl::getState(const std::string& sta
goto FOUND;
LOG(ERROR) << "No state with id " << stateId << " found!";
-
+ {
+ Event ev;
+ ev.name = "error.execution";
+ ev.eventType = Event::PLATFORM;
+ ev.data.compound["cause"] = Data("No state with id " + stateId + " found", Data::VERBATIM);
+ throw ev;
+ }
FOUND:
if (target.size() > 0) {
for (int i = 0; i < target.size(); i++) {
@@ -1726,8 +1742,10 @@ NodeSet<std::string> InterpreterImpl::getTargetStates(const Arabica::DOM::Node<s
std::list<std::string> targetIds = InterpreterImpl::tokenizeIdRefs(ATTR(transition, "target"));
for (std::list<std::string>::const_iterator targetIter = targetIds.begin(); targetIter != targetIds.end(); targetIter++) {
Arabica::DOM::Node<std::string> state = getState(*targetIter);
- assert(HAS_ATTR(state, "id"));
- targetStates.push_back(state);
+ if (state) {
+ assert(HAS_ATTR(state, "id"));
+ targetStates.push_back(state);
+ }
}
return targetStates;
}
diff --git a/src/uscxml/Message.h b/src/uscxml/Message.h
index c444cad..9c17340 100644
--- a/src/uscxml/Message.h
+++ b/src/uscxml/Message.h
@@ -318,6 +318,23 @@ public:
return false;
}
+ static bool getParam(params_t params, const std::string& name, bool& target) {
+ if (params.find(name) != params.end()) {
+ target = true;
+ if (iequals(params.find(name)->second.atom, "false")) {
+ target = false;
+ } else if(iequals(params.find(name)->second.atom, "off")) {
+ target = false;
+ } else if(iequals(params.find(name)->second.atom, "no")) {
+ target = false;
+ } else if(iequals(params.find(name)->second.atom, "0")) {
+ target = false;
+ }
+ return true;
+ }
+ return false;
+ }
+
template <typename T> static bool getParam(params_t params, const std::string& name, std::list<T>& target) {
if (params.find(name) != params.end()) {
std::pair<params_t::iterator, params_t::iterator> rangeIter = params.equal_range(name);
diff --git a/src/uscxml/URL.cpp b/src/uscxml/URL.cpp
index 2aaa46b..7948b3b 100644
--- a/src/uscxml/URL.cpp
+++ b/src/uscxml/URL.cpp
@@ -757,6 +757,7 @@ void URLFetcher::perform() {
case CURLM_BAD_HANDLE:
case CURLM_BAD_EASY_HANDLE:
case CURLE_FILE_COULDNT_READ_FILE:
+ case CURLE_COULDNT_CONNECT:
case CURLM_OUT_OF_MEMORY:
case CURLM_INTERNAL_ERROR:
case CURLM_BAD_SOCKET:
diff --git a/src/uscxml/plugins/invoker/CMakeLists.txt b/src/uscxml/plugins/invoker/CMakeLists.txt
index 6dd78ec..607e64e 100644
--- a/src/uscxml/plugins/invoker/CMakeLists.txt
+++ b/src/uscxml/plugins/invoker/CMakeLists.txt
@@ -95,6 +95,26 @@ else()
endif()
+# expect invoker
+
+set(USCXML_INVOKERS "expect ${USCXML_INVOKERS}")
+file(GLOB_RECURSE EXPECT_INVOKER
+ expect/*.cpp
+ expect/*.h
+)
+if (BUILD_AS_PLUGINS)
+ source_group("" FILES EXPECT_INVOKER)
+ add_library(
+ invoker_expect SHARED
+ ${EXPECT_INVOKER}
+ "../Plugins.cpp")
+ target_link_libraries(invoker_expect uscxml)
+ set_target_properties(invoker_expect PROPERTIES FOLDER "Plugin Invoker")
+else()
+ list (APPEND USCXML_FILES ${EXPECT_INVOKER})
+endif()
+
+
# SQLite3 SQL Invoker
if (SQLITE3_FOUND)
diff --git a/src/uscxml/plugins/invoker/expect/ExpectInvoker.cpp b/src/uscxml/plugins/invoker/expect/ExpectInvoker.cpp
new file mode 100644
index 0000000..c66005f
--- /dev/null
+++ b/src/uscxml/plugins/invoker/expect/ExpectInvoker.cpp
@@ -0,0 +1,359 @@
+/**
+ * @file
+ * @author 2012-2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
+ * @copyright Simplified BSD
+ *
+ * @cond
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the FreeBSD license as published by the FreeBSD
+ * project.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the FreeBSD license along with this
+ * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
+ * @endcond
+ */
+
+#include "ExpectInvoker.h"
+#include <glog/logging.h>
+
+#ifdef BUILD_AS_PLUGINS
+#include <Pluma/Connector.hpp>
+#endif
+
+#include "uscxml/UUID.h"
+
+#undef USE_TCL_STUBS
+
+namespace uscxml {
+
+#ifdef BUILD_AS_PLUGINS
+PLUMA_CONNECTOR
+bool pluginConnect(pluma::Host& host) {
+ host.add( new ExpectInvokerProvider() );
+ return true;
+}
+#endif
+
+Tcl_Interp* ExpectInvoker::_tcl = NULL;
+
+ExpectInvoker::ExpectInvoker() : _eventQueue(NULL) {
+}
+
+ExpectInvoker::~ExpectInvoker() {
+ _eventQueue->stop();
+ exp_cl
+// if (_tcl) {
+// Tcl_DeleteInterp(_tcl);
+// }
+};
+
+boost::shared_ptr<InvokerImpl> ExpectInvoker::create(InterpreterImpl* interpreter) {
+ boost::shared_ptr<ExpectInvoker> invoker = boost::shared_ptr<ExpectInvoker>(new ExpectInvoker());
+ return invoker;
+}
+
+Data ExpectInvoker::getDataModelVariables() {
+ Data data;
+ return data;
+}
+
+void ExpectInvoker::send(const SendRequest& req) {
+ EventContext* ctx = new EventContext();
+ ctx->sendReq = req;
+ ctx->instance = this;
+
+// LOG(ERROR) << "################ " << req;
+
+ std::string eventId = UUID::getUUID();
+ _eventQueue->addEvent(eventId, ExpectInvoker::send, 0, ctx);
+
+// send(ctx, "");
+}
+
+void ExpectInvoker::send(void *userdata, const std::string event) {
+
+ EventContext* ctx = (EventContext*)userdata;
+ if (!ctx)
+ return;
+
+ if (!ctx->instance) {
+ delete(ctx);
+ return;
+ }
+
+ const SendRequest& req = ctx->sendReq;
+
+ if (iequals(req.name, "expect.match")) {
+ int nrCases = req.params.size();
+ struct exp_case *cases = (struct exp_case*)malloc(sizeof(struct exp_case) * (nrCases + 1));
+ memset(cases, 0, sizeof(exp_case) * (nrCases + 1));
+
+ /**
+ exp_end: indicates that no more patterns appear.
+ exp_glob: indicates that the pattern is a glob-style string pattern.
+ exp_exact: indicates that the pattern is an exact string.
+ exp_regexp: indicates that the pattern is a regexp-style string pattern.
+ exp_compiled: indicates that the pattern is a regexp-style string pattern, and that its compiled form is also provided.
+ exp_null: indicates that the pattern is a null (for debugging purposes, a string pattern must also follow).
+ */
+
+ Event::params_t::const_iterator paramIter = req.params.begin();
+ int index = 0;
+ while (paramIter != req.params.end()) {
+ struct exp_case* expCase = &cases[index];
+ size_t colonPos = paramIter->first.find(":");
+ if (colonPos != std::string::npos) {
+ if (paramIter->first.substr(0, colonPos) == "regex") {
+ expCase->type = exp_regexp;
+ } else if(paramIter->first.substr(0, colonPos) == "glob") {
+ expCase->type = exp_glob;
+ } else if(paramIter->first.substr(0, colonPos) == "exact") {
+ expCase->type = exp_exact;
+ } else {
+ // if we can't make sense of the type
+ expCase->type = exp_exact;
+ }
+ } else {
+ expCase->type = exp_regexp;
+ }
+
+ expCase->pattern = strdup(paramIter->second.atom.c_str());
+// LOG(ERROR) << "################ " << expCase->pattern;
+
+ if (expCase->type == exp_regexp) {
+ expCase->re = TclRegComp(expCase->pattern);
+ if (expCase->re == NULL) {
+ LOG(ERROR) << TclGetRegError();
+ expCase->type = exp_null;
+ }
+ }
+ expCase->value = index + 1;
+ paramIter++;
+ index++;
+ }
+
+ assert(index == nrCases);
+
+ cases[nrCases].type = exp_end;
+
+ /**
+ * The functions wait until the output from a process matches one of the
+ * patterns, a specified time period has passed, or an EOF is seen.
+ */
+
+ int rc = 0;
+ // exp_fexpectv won't return on timeout when called in thread
+// rc = exp_fexpectv(ctx->instance->_cmdFP, cases);
+ rc = exp_expectv(ctx->instance->_cmdFD, cases);
+
+ if (rc == EXP_EOF) {
+ Event ev;
+ ev.name = "expect.match.eof";
+ ev.data.compound["buffer"] = Data(exp_buffer, Data::VERBATIM);
+ ctx->instance->returnEvent(ev);
+ } else if (rc == EXP_TIMEOUT) {
+ Event ev;
+ ev.name = "expect.match.timeout";
+ ev.data.compound["buffer"] = Data(exp_buffer, Data::VERBATIM);
+ ctx->instance->returnEvent(ev);
+ } else if (rc == EXP_FULLBUFFER) {
+ Event ev;
+ ev.name = "expect.match.fullbuffer";
+ ev.data.compound["buffer"] = Data(exp_buffer, Data::VERBATIM);
+ ctx->instance->returnEvent(ev);
+ } else if (rc > 0) {
+ rc--; // we started at 1
+ paramIter = req.params.begin();
+ while(rc > 0) {
+ if (paramIter == req.params.end())
+ break;
+ paramIter++;
+ rc--;
+ }
+ if (paramIter != req.params.end()) {
+ Event event;
+
+ size_t colonPos = paramIter->first.find(":");
+ if (colonPos != std::string::npos) {
+ std::string eventName = paramIter->first;
+ event.name = std::string("expect.match.") + eventName.substr(colonPos + 1, eventName.length() - (colonPos + 1));
+ event.data.compound["type"] = Data(paramIter->first.substr(0, colonPos), Data::VERBATIM);
+
+ } else {
+ event.name = std::string("expect.match.") + paramIter->first;
+ event.data.compound["type"] = Data("regex", Data::VERBATIM);
+ }
+
+ event.data.compound["pattern"] = Data(paramIter->second.atom, Data::VERBATIM);
+ event.data.compound["buffer"] = Data(exp_buffer, Data::VERBATIM);
+ event.data.compound["start"] = Data((int)(exp_match - exp_buffer));
+ event.data.compound["end"] = Data((int)(exp_match_end - exp_buffer));
+ event.data.compound["match"] = Data(std::string(exp_buffer).substr(exp_match - exp_buffer, exp_match_end - exp_match), Data::VERBATIM);
+ ctx->instance->returnEvent(event);
+ } else {
+ // exp_fexpectl returned gibberish
+ assert(false);
+ }
+ } else {
+ // exp_fexpectl returned gibberish
+ assert(false);
+ }
+
+ // free our memory
+ for (int i = 0; i < nrCases; i++) {
+ if (cases[i].pattern != NULL)
+ free(cases[i].pattern);
+ if (cases[i].re != NULL)
+ free(cases[i].re);
+ }
+ free(cases);
+
+ } else if (iequals(req.name, "expect.send")) {
+ std::string toSend = unescape(req.content);
+ ctx->instance->_interpreter->getDataModel().replaceExpressions(toSend);
+ fwrite(toSend.c_str(), toSend.length(), 1, ctx->instance->_cmdFP);
+ }
+
+ delete(ctx);
+}
+
+void ExpectInvoker::cancel(const std::string sendId) {
+}
+
+void ExpectInvoker::invoke(const InvokeRequest& req) {
+ if (_eventQueue == NULL) {
+ _eventQueue = new DelayedEventQueue();
+ _eventQueue->start();
+ }
+
+ EventContext* ctx = new EventContext();
+ ctx->invokeReq = req;
+ ctx->instance = this;
+
+ //_eventQueue->addEvent(req.sendid, ExpectInvoker::invoke, 0, ctx);
+ invoke(ctx, "");
+
+}
+
+void ExpectInvoker::invoke(void *userdata, const std::string event) {
+ EventContext* ctx = (EventContext*)userdata;
+
+ if (!ctx)
+ return;
+
+ if (!ctx->instance) {
+ delete(ctx);
+ return;
+ }
+
+ const InvokeRequest& req = ctx->invokeReq;
+
+ // moved here for thread local storage
+ if (ctx->instance->_tcl == NULL) {
+ ctx->instance->_tcl = Tcl_CreateInterp();
+ if (ctx->instance->_tcl) {
+ Tcl_Init(ctx->instance->_tcl);
+ Expect_Init(ctx->instance->_tcl);
+ }
+ ctx->instance->_cmdFP = NULL;
+
+ bool debug = false;
+ Event::getParam(req.params, "debug", debug);
+ if (debug) {
+ exp_is_debugging = 1;
+ } else {
+ exp_is_debugging = 0;
+ }
+
+ int timeout = 20;
+ Event::getParam(req.params, "timeout", timeout);
+ exp_timeout = timeout;
+
+ bool logUser = false;
+ Event::getParam(req.params, "loguser", logUser);
+ if (logUser) {
+ exp_loguser = 1;
+ } else {
+ exp_loguser = 0;
+ }
+
+ // exp_interactive = 1;
+ exp_logfile = 0;
+ // exp_remove_nulls = 1;
+ // exp_ttyinit = 1;
+
+ } else {
+// assert(false);
+ }
+
+ char* cmd = NULL;
+ char** args = NULL;
+ int nrArgs = 0;
+
+ if (req.params.count("spawn")) {
+ // get command
+ std::string command;
+ Event::getParam(req.params, "spawn", command);
+ cmd = strdup(command.c_str());
+
+ // get arguments
+ nrArgs = req.params.count("argument");
+ args = (char**)malloc(sizeof(char*) * nrArgs + 2);
+ args[0] = strdup(command.c_str());
+
+ size_t index = 1;
+ std::pair<Event::params_t::const_iterator, Event::params_t::const_iterator> argIterRange = req.params.equal_range("argument");
+ Event::params_t::const_iterator argIter = argIterRange.first;
+ while(argIter != argIterRange.second) {
+ args[index] = strdup(argIter->second.atom.c_str());
+ argIter++;
+ index++;
+ }
+ args[index] = (char*)0;
+ } else if(req.params.count("command")) {
+
+ }
+
+ // open socket
+ ctx->instance->_cmdFD = exp_spawnv(cmd, args);
+ if (ctx->instance->_cmdFD > 0) {
+ ctx->instance->_cmdFP = fdopen(ctx->instance->_cmdFD, "r+");
+
+ if (ctx->instance->_cmdFP) {
+ // disable buffering
+ setbuf(ctx->instance->_cmdFP,(char *)0);
+ Event event;
+ event.name = "spawn.success";
+ ctx->instance->returnEvent(event);
+ }
+ }
+
+ if (ctx->instance->_cmdFP == NULL || ctx->instance->_cmdFD <= 0) {
+ Event event;
+ event.name = "spawn.failed";
+ event.data.compound["cause"] = Data(strerror(errno), Data::VERBATIM);
+ Tcl_Obj *infoObj = Tcl_GetVar2Ex(_tcl, "errorInfo", NULL, TCL_GLOBAL_ONLY);
+ if (infoObj) {
+ event.data.compound["errorInfo"] = Data(Tcl_GetString(infoObj), Data::VERBATIM);
+ }
+
+ ctx->instance->returnEvent(event);
+ }
+
+ if (cmd)
+ free(cmd);
+
+ if (args) {
+ for (int i = 0; i < nrArgs + 1; i++) {
+ free(args[i]);
+ }
+ free(args);
+ }
+
+}
+
+} \ No newline at end of file
diff --git a/src/uscxml/plugins/invoker/expect/ExpectInvoker.h b/src/uscxml/plugins/invoker/expect/ExpectInvoker.h
new file mode 100644
index 0000000..7c9861e
--- /dev/null
+++ b/src/uscxml/plugins/invoker/expect/ExpectInvoker.h
@@ -0,0 +1,79 @@
+/**
+ * @file
+ * @author 2012-2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
+ * @copyright Simplified BSD
+ *
+ * @cond
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the FreeBSD license as published by the FreeBSD
+ * project.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the FreeBSD license along with this
+ * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
+ * @endcond
+ */
+
+#ifndef EXPECTINVOKER_H_W02590F0
+#define EXPECTINVOKER_H_W02590F0
+
+#include <uscxml/Interpreter.h>
+
+#ifdef BUILD_AS_PLUGINS
+#include "uscxml/plugins/Plugins.h"
+#endif
+
+#include <tcl.h>
+#include <expect_tcl.h>
+#include <expect.h>
+
+namespace uscxml {
+
+class ExpectInvoker : public InvokerImpl {
+public:
+ struct EventContext {
+ InvokeRequest invokeReq;
+ SendRequest sendReq;
+ ExpectInvoker* instance;
+ };
+
+ ExpectInvoker();
+ virtual ~ExpectInvoker();
+ virtual boost::shared_ptr<InvokerImpl> create(InterpreterImpl* interpreter);
+
+ virtual std::set<std::string> getNames() {
+ std::set<std::string> names;
+ names.insert("expect");
+ names.insert("http://uscxml.tk.informatik.tu-darmstadt.de/#expect");
+ return names;
+ }
+
+ virtual Data getDataModelVariables();
+ virtual void send(const SendRequest& req);
+ virtual void cancel(const std::string sendId);
+ virtual void invoke(const InvokeRequest& req);
+
+protected:
+
+ static void send(void *userdata, const std::string event);
+ static void invoke(void *userdata, const std::string event);
+
+ static Tcl_Interp* _tcl;
+ FILE* _cmdFP;
+ int _cmdFD;
+
+ DelayedEventQueue* _eventQueue;
+
+};
+
+#ifdef BUILD_AS_PLUGINS
+PLUMA_INHERIT_PROVIDER(ExpectInvoker, InvokerImpl);
+#endif
+
+}
+
+
+#endif /* end of include guard: EXPECTINVOKER_H_W02590F0 */
diff --git a/src/uscxml/plugins/invoker/im/IMInvoker.cpp b/src/uscxml/plugins/invoker/im/IMInvoker.cpp
index 981f2fe..23594f3 100644
--- a/src/uscxml/plugins/invoker/im/IMInvoker.cpp
+++ b/src/uscxml/plugins/invoker/im/IMInvoker.cpp
@@ -572,14 +572,6 @@ Data IMInvoker::purpleValueToData(GValue* value) {
IMInvoker::IMInvoker() {
_account = NULL;
- if (!_eventQueue) {
- tthread::lock_guard<tthread::mutex> lock(_initMutex);
- _eventQueue = new DelayedEventQueue();
- _eventQueue->addEvent("initLibPurple", IMInvoker::initLibPurple, 0, NULL);
- _eventQueue->start();
- // make sure to have the shebang initialized when we leave
- _initCond.wait(_initMutex);
- }
}
IMInvoker::~IMInvoker() {
@@ -593,6 +585,16 @@ IMInvoker::~IMInvoker() {
boost::shared_ptr<InvokerImpl> IMInvoker::create(InterpreterImpl* interpreter) {
boost::shared_ptr<IMInvoker> invoker = boost::shared_ptr<IMInvoker>(new IMInvoker());
+
+ if (!_eventQueue) {
+ tthread::lock_guard<tthread::mutex> lock(_initMutex);
+ _eventQueue = new DelayedEventQueue();
+ _eventQueue->addEvent("initLibPurple", IMInvoker::initLibPurple, 0, NULL);
+ _eventQueue->start();
+ // make sure to have the shebang initialized when we leave
+ _initCond.wait(_initMutex);
+ }
+
invoker->_dataModelVars.compound["plugins"] = _pluginData;
return invoker;
}
diff --git a/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp b/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp
index 1b3eff0..7a24c79 100644
--- a/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp
+++ b/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp
@@ -21,6 +21,7 @@
#include "uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.h"
#include "uscxml/Message.h"
+#include "uscxml/DOMUtils.h"
#include <iostream>
#include <event2/dns.h>
#include <event2/buffer.h>
@@ -85,8 +86,26 @@ boost::shared_ptr<IOProcessorImpl> BasicHTTPIOProcessor::create(InterpreterImpl*
Data BasicHTTPIOProcessor::getDataModelVariables() {
Data data;
- assert(_url.length() > 0);
+
+ // we are not connected!
+ if(_url.length() == 0)
+ return data;
+
data.compound["location"] = Data(_url, Data::VERBATIM);
+
+ URL url(_url);
+ data.compound["host"] = Data(url.host(), Data::VERBATIM);
+ data.compound["port"] = Data(url.port(), Data::VERBATIM);
+ data.compound["path"] = Data(url.path(), Data::VERBATIM);
+ data.compound["scheme"] = Data(url.scheme(), Data::VERBATIM);
+
+ std::vector<std::string> pathComps = url.pathComponents();
+ std::vector<std::string>::const_iterator pathCompIter = pathComps.begin();
+ while(pathCompIter != pathComps.end()) {
+ data.compound["pathComponens"].array.push_back(Data(*pathCompIter, Data::VERBATIM));
+ pathCompIter++;
+ }
+
return data;
}
@@ -94,10 +113,29 @@ bool BasicHTTPIOProcessor::httpRecvRequest(const HTTPServer::Request& req) {
Event reqEvent = req;
reqEvent.eventType = Event::EXTERNAL;
+// std::cout << req.raw << std::endl;
+
+ /**
+ * If a single instance of the parameter '_scxmleventname' is present, the
+ * SCXML Processor must use its value as the name of the SCXML event that it
+ * raises.
+ */
+
// this will call the const subscript operator
if (req.data["content"]["_scxmleventname"]) {
reqEvent.name = req.data["content"]["_scxmleventname"].atom;
}
+ if (req.data["content"]["content"]) {
+ reqEvent.content = req.data["content"]["content"].atom;
+ }
+
+ // check whether we can parse it as XML
+ if (reqEvent.content.length() > 0) {
+ NameSpacingParser parser = NameSpacingParser::fromXML(reqEvent.content);
+ if (!parser.errorsReported()) {
+ reqEvent.dom = parser.getDocument();
+ }
+ }
/// test532
if (reqEvent.name.length() == 0)
diff --git a/src/uscxml/server/HTTPServer.cpp b/src/uscxml/server/HTTPServer.cpp
index de78ba9..804b025 100644
--- a/src/uscxml/server/HTTPServer.cpp
+++ b/src/uscxml/server/HTTPServer.cpp
@@ -77,6 +77,12 @@ HTTPServer::HTTPServer(unsigned short port, unsigned short wsPort, SSLConfig* ss
_httpHandle = NULL;
_wsHandle = NULL;
+#ifdef _WIN32
+ _wsHandle = NULL;
+#else
+ _wsHandle = 0;
+#endif
+
determineAddress();
unsigned int allowedMethods =
@@ -525,6 +531,10 @@ void HTTPServer::replyCallback(evutil_socket_t fd, short what, void *arg) {
bool HTTPServer::registerServlet(const std::string& path, HTTPServlet* servlet) {
HTTPServer* INSTANCE = getInstance();
+
+ if (!INSTANCE->_httpHandle)
+ return true;
+
tthread::lock_guard<tthread::recursive_mutex> lock(INSTANCE->_mutex);
// remove trailing and leading slash
@@ -560,6 +570,10 @@ bool HTTPServer::registerServlet(const std::string& path, HTTPServlet* servlet)
bool HTTPServer::registerServlet(const std::string& path, WebSocketServlet* servlet) {
HTTPServer* INSTANCE = getInstance();
+
+ if (!INSTANCE->_wsHandle)
+ return true;
+
tthread::lock_guard<tthread::recursive_mutex> lock(INSTANCE->_mutex);
// remove trailing and leading slash
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 4c906fd..4f1ec70 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -11,11 +11,11 @@ if (NOT WIN32)
endif()
add_test(test-execution ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/uscxml-browser ${CMAKE_SOURCE_DIR}/test/samples/uscxml/test-execution.scxml)
-add_test(test-communication ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/uscxml-browser ${CMAKE_SOURCE_DIR}/test/samples/uscxml/test-communication.scxml)
+add_test(test-communication ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/uscxml-browser -t5493 ${CMAKE_SOURCE_DIR}/test/samples/uscxml/test-communication.scxml)
add_test(test-done-data ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/uscxml-browser ${CMAKE_SOURCE_DIR}/test/samples/uscxml/test-donedata.scxml)
if (SWI_FOUND)
- add_test(test-prolog-swi ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/uscxml-browser ${CMAKE_SOURCE_DIR}/test/samples/uscxml/test-prolog.scxml)
+ add_test(test-prolog-swi ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/uscxml-browser -t2345 ${CMAKE_SOURCE_DIR}/test/samples/uscxml/test-prolog.scxml)
endif()
# if (FFMPEG_FOUND)
@@ -68,21 +68,21 @@ if (NOT WIN32)
add_test(test-arabica-xpath ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-arabica-parsing)
set_target_properties(test-arabica-parsing PROPERTIES FOLDER "Tests")
- add_executable(test-stress src/test-stress.cpp)
- if (BUILD_AS_PLUGINS)
- target_link_libraries(test-stress uscxml invoker_dirmon)
- else()
- target_link_libraries(test-stress uscxml)
- endif()
- add_test(test-stress ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-stress ${CMAKE_SOURCE_DIR}/test/samples/w3c)
- set_target_properties(test-stress PROPERTIES FOLDER "Tests")
-
- if (LIBPURPLE_FOUND)
- add_executable(test-instant-messaging src/test-instant-messaging.cpp)
- target_link_libraries(test-instant-messaging uscxml ${LIBPURPLE_LIBRARY} ${GLIB2_LIBRARIES} ${ICONV_LIBRARIES})
- add_test(test-instant-messaging ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-instant-messaging)
- set_target_properties(test-instant-messaging PROPERTIES FOLDER "Tests")
- endif()
+ # add_executable(test-stress src/test-stress.cpp)
+ # if (BUILD_AS_PLUGINS)
+ # target_link_libraries(test-stress uscxml invoker_dirmon)
+ # else()
+ # target_link_libraries(test-stress uscxml)
+ # endif()
+ # add_test(test-stress ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-stress ${CMAKE_SOURCE_DIR}/test/samples/w3c)
+ # set_target_properties(test-stress PROPERTIES FOLDER "Tests")
+
+ # if (LIBPURPLE_FOUND)
+ # add_executable(test-instant-messaging src/test-instant-messaging.cpp)
+ # target_link_libraries(test-instant-messaging uscxml ${LIBPURPLE_LIBRARY} ${GLIB2_LIBRARIES} ${ICONV_LIBRARIES})
+ # add_test(test-instant-messaging ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-instant-messaging)
+ # set_target_properties(test-instant-messaging PROPERTIES FOLDER "Tests")
+ # endif()
endif()
add_executable(test-url src/test-url.cpp)
@@ -123,6 +123,13 @@ set_target_properties(scxml-test-framework-client PROPERTIES FOLDER "Tests")
# add_test(test-curl-multi-api ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-completion)
# set_target_properties(test-curl-multi-api PROPERTIES FOLDER "Tests")
+if (EXPECT_FOUND AND TCL_FOUND)
+ add_executable(test-expect
+ src/test-expect.cpp)
+ target_link_libraries(test-expect uscxml)
+ set_target_properties(test-expect PROPERTIES FOLDER "Tests")
+endif()
+
add_executable(test-w3c src/test-w3c.cpp)
target_link_libraries(test-w3c uscxml)
set_target_properties(test-w3c PROPERTIES FOLDER "Tests")
@@ -133,15 +140,23 @@ file(GLOB_RECURSE W3C_TESTS
foreach( W3C_TEST ${W3C_TESTS} )
string(REGEX MATCH "[^//]+/[^//]+.scxml" TEST_NAME ${W3C_TEST})
- #message("TEST_NAME: ${TEST_NAME}")
+# message("TEST_NAME: ${TEST_NAME}")
if (NOT TEST_NAME MATCHES ".*sub.*")
if (BUILD_TESTS_W3C_ECMA AND TEST_NAME MATCHES "^ecma\\/.*")
add_test(${TEST_NAME} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-w3c ${W3C_TEST})
- set_tests_properties(${TEST_NAME} PROPERTIES FAIL_REGULAR_EXPRESSION "TEST FAILED")
+# set_tests_properties(${TEST_NAME} PROPERTIES FAIL_REGULAR_EXPRESSION "TEST FAILED")
+ if (TEST_NAME STREQUAL "ecma/test250.scxml")
+ set_tests_properties(${TEST_NAME} PROPERTIES
+ FAIL_REGULAR_EXPRESSION "entering final state, invocation was not cancelled")
+ elseif (TEST_NAME STREQUAL "ecma/test307.scxml")
+ set_tests_properties(${TEST_NAME} PROPERTIES
+ FAIL_REGULAR_EXPRESSION "error in state")
+ endif()
+
endif()
if (BUILD_TESTS_W3C_XPATH AND TEST_NAME MATCHES "^xpath\\/.*")
add_test(${TEST_NAME} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-w3c ${W3C_TEST})
- set_tests_properties(${TEST_NAME} PROPERTIES FAIL_REGULAR_EXPRESSION "TEST FAILED")
+# set_tests_properties(${TEST_NAME} PROPERTIES FAIL_REGULAR_EXPRESSION "TEST FAILED")
endif()
endif()
endforeach()
diff --git a/test/samples/uscxml/test-expect.scxml b/test/samples/uscxml/test-expect.scxml
new file mode 100644
index 0000000..abd2a58
--- /dev/null
+++ b/test/samples/uscxml/test-expect.scxml
@@ -0,0 +1,64 @@
+<scxml datamodel="ecmascript">
+ <script src="http://uscxml.tk.informatik.tu-darmstadt.de/scripts/dump.js" />
+ <state id="start">
+ <state id="spawn">
+ <invoke type="expect" id="telnet">
+ <param name="debug" expr="false" />
+ <param name="timeout" expr="3" />
+ <param name="loguser" expr="false" />
+ <param name="spawn" expr="'/usr/bin/telnet'" />
+ <param name="argument" expr="_ioprocessors.basichttp.host" />
+ <param name="argument" expr="_ioprocessors.basichttp.port" />
+ <!-- <param name="argument" expr="80" /> -->
+ <finalize><script>//print('------------'); dump(_event);</script></finalize>
+ </invoke>
+
+ <state id="idle">
+ <onentry><log expr="'Waiting'"/></onentry>
+ <transition event="spawn.success" target="spawned" />
+ <transition event="spawn.failed" target="exit">
+ <script>print('FAIL: '); dump(_event);</script>
+ </transition>
+ </state>
+
+ <state id="spawned">
+ <onentry>
+ <log expr="'Spawned'"/>
+ <send target="#_telnet" event="expect.match">
+ <param name="exact:refused" expr="'Connection refused'" />
+ <!-- last line of a telnet connect -->
+ <param name="exact:connected" expr="'Escape character is \'^]\'.'" />
+ </send>
+ </onentry>
+ <transition event="expect.match.connected" target="established" />
+ </state>
+
+ <state id="established">
+ <onentry>
+ <log expr="'Established'"/>
+ <send target="#_telnet" event="expect.send">
+ <content>GET /${_name} HTTP/1.1\n\n</content>
+ </send>
+ <send target="#_telnet" event="expect.match">
+ <param name="exact:answered" expr="'Some arbitrary content'" />
+ </send>
+ </onentry>
+
+ <!-- Answer the telnet request -->
+ <transition event="http.get" type="internal">
+ <respond to="_event.origin">
+ <header name="Content-Type" value="text/plain" />
+ <content>Some arbitrary content</content>
+ </respond>
+ </transition>
+
+ <transition event="expect.match.answered" target="start" type="external" />
+
+ </state>
+
+ </state>
+ </state>
+
+
+ <final id="exit" />
+</scxml> \ No newline at end of file
diff --git a/test/src/test-expect.cpp b/test/src/test-expect.cpp
new file mode 100644
index 0000000..51c3bcb
--- /dev/null
+++ b/test/src/test-expect.cpp
@@ -0,0 +1,58 @@
+#include <tcl.h>
+#include <expect_tcl.h>
+#include <expect.h>
+#include <stdlib.h>
+
+int main(int argc, char** argv) {
+ int rc = 0;
+
+ Tcl_Interp *interp = Tcl_CreateInterp();
+ Tcl_FindExecutable(argv[0]);
+
+ if (Tcl_Init(interp) == TCL_ERROR) {
+ fprintf(stderr,"Tcl_Init failed: %s\n",Tcl_GetStringResult (interp));
+ (void) exit(1);
+ }
+
+ if (Expect_Init(interp) == TCL_ERROR) {
+ fprintf(stderr,"Expect_Init failed: %s\n",Tcl_GetStringResult (interp));
+ (void) exit(1);
+ }
+
+ exp_loguser = 1;
+ exp_is_debugging = 1;
+ exp_timeout = 3;
+
+ FILE *fp;
+ int ec;
+// char* program = "/usr/bin/telnet localhost 80";
+// if (0 > (ec = exp_spawnl("sh","sh","-c",program,(char *)0)))
+// exit(0);
+// if (NULL == (fp = fdopen(ec,"r+")))
+// exit(0);
+// setbuf(fp,(char *)0);
+
+ if (0 > (ec = exp_spawnl("/usr/bin/telnet", "/usr/bin/telnet","localhost", "80", (char *)0)))
+ exit(0);
+ if (NULL == (fp = fdopen(ec,"r+")))
+ exit(0);
+ setbuf(fp,(char *)0);
+
+ switch (exp_fexpectl(fp,
+ exp_glob, "qConnected to", 1,
+ exp_glob, "qConnection failed", 2,
+ exp_end)) {
+ case 1:
+ printf("SUCCESS!");
+ fprintf(fp, "%s\r", "GET /");
+
+ break;
+ case 2:
+ printf("FAIL!");
+ break;
+
+ default:
+ break;
+ }
+ exit(EXIT_SUCCESS);
+} \ No newline at end of file
diff --git a/test/src/test-url.cpp b/test/src/test-url.cpp
index e0f8343..9c4d941 100644
--- a/test/src/test-url.cpp
+++ b/test/src/test-url.cpp
@@ -82,17 +82,23 @@ int main(int argc, char** argv) {
#endif
{
- URL url(argv[0]);
- assert(canResolve(argv[0]));
- assert(canResolve(url.asString()));
-
- URL baseUrl = URL::asBaseURL(url);
- URL exeUrl(exeName);
- exeUrl.toAbsolute(baseUrl);
- assert(canResolve(exeUrl.asString()));
- std::cout << exeUrl.asString() << std::endl;
- exeUrl.download(true);
- assert(exeUrl.getInContent().length() > 0);
+ try {
+
+ URL url(argv[0]);
+ assert(canResolve(argv[0]));
+ assert(canResolve(url.asString()));
+
+ URL baseUrl = URL::asBaseURL(url);
+ URL exeUrl(exeName);
+ exeUrl.toAbsolute(baseUrl);
+ assert(canResolve(exeUrl.asString()));
+ std::cout << exeUrl.asString() << std::endl;
+ exeUrl.download(true);
+ assert(exeUrl.getInContent().length() > 0);
+
+ } catch (Event e) {
+ std::cout << e << std::endl;
+ }
}
{