From 680c646011ec55dd4c639a5b61d8c42a10272ae2 Mon Sep 17 00:00:00 2001 From: deva Date: Fri, 24 Jul 2009 16:05:18 +0000 Subject: More extensive testing, and documentation in the header files. --- server/src/Makefile.am | 12 ++--- server/src/luaquerymapper.cc | 121 +++++++++++++++++++++++++++++++------------ server/src/luaquerymapper.h | 36 +++++++++---- server/src/queryparser.cc | 72 ++++++++++++++++++++----- server/src/queryparser.h | 32 +++++++++--- 5 files changed, 203 insertions(+), 70 deletions(-) diff --git a/server/src/Makefile.am b/server/src/Makefile.am index 3fa0aad..dbc481a 100644 --- a/server/src/Makefile.am +++ b/server/src/Makefile.am @@ -130,7 +130,7 @@ test: $(TESTFILES) @echo "All tests done." test_clean: - rm -f $(TESTFILES) + rm -f $(TESTFILES) $(TESTLOGS) TEST_VERSIONSTR_FILES = \ versionstr.cc \ @@ -168,8 +168,6 @@ test_queryhandlerpracro: $(TEST_QUERYHANDLERPRACRO_FILES) TEST_QUERYPARSER_FILES = \ queryparser.cc \ - queryhandlerpentominos.cc \ - tcpsocket.cc \ $(BASICFILES) \ $(PARSERFILES) test_queryparser: $(TEST_QUERYPARSER_FILES) @@ -177,13 +175,9 @@ test_queryparser: $(TEST_QUERYPARSER_FILES) TEST_LUAQUERMAPPER_FILES = \ luaquerymapper.cc \ - queryparser.cc \ - queryhandlerpentominos.cc \ - tcpsocket.cc \ - $(BASICFILES) \ - $(PARSERFILES) + $(BASICFILES) test_luaquerymapper: $(TEST_LUAQUERMAPPER_FILES) - @../../tools/test $(TEST_LUAQUERMAPPER_FILES) $(LUA_LIBS) $(BASICFLAGS) $(PARSERFLAGS) + @../../tools/test $(TEST_LUAQUERMAPPER_FILES) $(LUA_LIBS) $(BASICFLAGS) TEST_TEMPLATEPARSER_FILES = \ templateparser.cc \ diff --git a/server/src/luaquerymapper.cc b/server/src/luaquerymapper.cc index d6ec531..7ee43ff 100644 --- a/server/src/luaquerymapper.cc +++ b/server/src/luaquerymapper.cc @@ -28,8 +28,6 @@ #include -#include "exception.h" - static std::string loadresultstring(QueryResult &res, std::string group = "") { std::string s; @@ -46,7 +44,7 @@ static std::string loadresultstring(QueryResult &res, std::string group = "") std::map< std::string, QueryResult >::iterator g = res.groups.begin(); while(g != res.groups.end()) { - if(group + (*g).first != "") s += group + (*g).first + " = {}\n"; + s += group + (*g).first + " = {}\n"; s += loadresultstring((*g).second, group + (*g).first + "."); g++; } @@ -54,8 +52,10 @@ static std::string loadresultstring(QueryResult &res, std::string group = "") return s; } -LUAQueryMapper::LUAQueryMapper() +LUAQueryMapper::LUAQueryMapper() throw(Exception) { + clean_top = -1; + L = luaL_newstate(); if(L == NULL) { error("Could not create LUA state."); @@ -69,16 +69,15 @@ LUAQueryMapper::LUAQueryMapper() LUAQueryMapper::~LUAQueryMapper() { - lua_close(L); + if(L) lua_close(L); } -void LUAQueryMapper::addQueryResult(QueryResult &result) +void LUAQueryMapper::addQueryResult(QueryResult &result) throw(Exception) { std::string preload = loadresultstring(result); PRACRO_DEBUG(querymapper, "Preload:\n%s\n", preload.c_str()); - if(luaL_loadbuffer(L, preload.c_str(), preload.size(), "preload")) { error(lua_tostring(L, lua_gettop(L))); return; @@ -93,7 +92,7 @@ void LUAQueryMapper::addQueryResult(QueryResult &result) clean_top = lua_gettop(L); } -Value LUAQueryMapper::map(const std::string &mapper) +Value LUAQueryMapper::map(const std::string &mapper) throw(Exception) { Value v; @@ -123,17 +122,13 @@ Value LUAQueryMapper::map(const std::string &mapper) // Check if app messed up the stack. if(lua_gettop(L) != clean_top + 3) { - error("Wrong number of return values in " + mapper); - lua_pop(L, lua_gettop(L) - clean_top); + error("Wrong number of return values (should be value, timestamp, source) in " + mapper); return v; } - // value, timestamp, source - // Check if the types are right if(lua_isstring(L, lua_gettop(L)) == false) { error("Source is not a string in " + mapper); - lua_pop(L, 3); return v; } v.source = lua_tostring(L, lua_gettop(L)); @@ -142,7 +137,6 @@ Value LUAQueryMapper::map(const std::string &mapper) // Check if the types are right if(lua_isnumber(L, lua_gettop(L)) == false) { error("Timestamp is not an integer in " + mapper); - lua_pop(L, 2); return v; } v.timestamp = lua_tointeger(L, lua_gettop(L)); @@ -151,7 +145,6 @@ Value LUAQueryMapper::map(const std::string &mapper) // Check if the types are right if(lua_isstring(L, lua_gettop(L)) == false) { error("Value is not a string in " + mapper); - lua_pop(L, 1); return v; } v.value = lua_tostring(L, lua_gettop(L)); @@ -163,42 +156,106 @@ Value LUAQueryMapper::map(const std::string &mapper) return v; } -void LUAQueryMapper::error(std::string message) +void LUAQueryMapper::error(std::string message) throw(Exception) { + if(clean_top != -1) lua_pop(L, lua_gettop(L) - clean_top); // Clean up stack throw Exception("ERROR in LUAQueryMapper: " + message); } #ifdef TEST_LUAQUERYMAPPER -#include "queryhandlerpentominos.h" -#include "queryparser.h" -#include "tcpsocket.h" - int main() { - TCPSocket s; - s.connect("localhost", 11108); - - QueryHandlerPentominos qh(&s, "2003791613"); + QueryResult res; - Query q1; - q1.attributes["device_id"] = "lensmeter"; - q1.attributes["device_type"] = "lensmeter"; + time_t now = time(NULL); - QueryResult res = qh.exec(q1); + res.groups["test"].timestamp = now; + res.groups["test"].source = "test app"; + res.groups["test"].values["somevalue"] = "hello world"; + res.groups["test"].values["pi"] = "3.1416"; printf("%s\n", loadresultstring(res).c_str()); LUAQueryMapper mapper; mapper.addQueryResult(res); - std::string luamap = "return status.errstr.value, 0, 0"; + // Test simple value forwarding + std::string luamap = "return test.somevalue.value, test.somevalue.timestamp, test.somevalue.source"; Value value = mapper.map(luamap); - printf("%s : %s, %lu\n", luamap.c_str(), value.value.c_str(), value.timestamp); + printf("%s =>\n %s, %lu, %s\n", luamap.c_str(), value.value.c_str(), value.timestamp, value.source.c_str()); + if(value.value != "hello world" || value.timestamp != now || value.source != "test app") + return 1; + + // Do some calculations + luamap = "return 2 * tonumber(test.pi.value), test.pi.timestamp, test.pi.source"; + value = mapper.map(luamap); + printf("%s =>\n %s, %lu, %s\n", luamap.c_str(), value.value.c_str(), value.timestamp, value.source.c_str()); + if(value.value != "6.2832" || value.timestamp != now || value.source != "test app") + return 1; + + // Attempt to access nonexisting value (should throw an exception) + try { + luamap = "return test.somevalue2.value, test.somevalue2.timestamp, test.somevalue2.source"; + value = mapper.map(luamap); + printf("%s =>\n %s, %lu, %s\n", luamap.c_str(), value.value.c_str(), value.timestamp, value.source.c_str()); + if(value.value != "hello world" || value.timestamp != now || value.source != "test app") + return 1; + } catch(Exception &e) { + printf("ERROR: %s\n", e.what()); + goto onandon; + } + return 1; + onandon: + + // Attempt to access nonexisting group (should throw an exception) + try { + luamap = "return test2.somevalue.value, test2.somevalue.timestamp, test2.somevalue.source"; + value = mapper.map(luamap); + printf("%s =>\n %s, %lu, %s\n", luamap.c_str(), value.value.c_str(), value.timestamp, value.source.c_str()); + if(value.value != "hello world" || value.timestamp != now || value.source != "test app") + return 1; + } catch(Exception &e) { + printf("ERROR: %s\n", e.what()); + goto stillonandon; + } + return 1; + stillonandon: + + // Switch order of return vars (should throw an exception) + try { + luamap = "return test.somevalue.source, test.somevalue.value, test.somevalue.timestamp"; + value = mapper.map(luamap); + printf("%s =>\n %s, %lu, %s\n", luamap.c_str(), value.value.c_str(), value.timestamp, value.source.c_str()); + if(value.value != "hello world" || value.timestamp != now || value.source != "test app") + return 1; + } catch(Exception &e) { + printf("ERROR: %s\n", e.what()); + goto onandonagain; + } + return 1; + onandonagain: + + // Syntax error (should throw an exception) + try { + luamap = "this(is{] not() - a != legal lua program!]"; + value = mapper.map(luamap); + printf("%s =>\n %s, %lu, %s\n", luamap.c_str(), value.value.c_str(), value.timestamp, value.source.c_str()); + if(value.value != "hello world" || value.timestamp != now || value.source != "test app") + return 1; + } catch(Exception &e) { + printf("ERROR: %s\n", e.what()); + goto stillonandonagain; + } + return 1; + stillonandonagain: - luamap = "return math.sin(status.errstr.timestamp) * 2, 0, 0"; + // And finally test if we haven't broken enything while being hostile to the lua engine... + luamap = "return test.somevalue.value, test.somevalue.timestamp, test.somevalue.source"; value = mapper.map(luamap); - printf("%s : %s, %lu\n", luamap.c_str(), value.value.c_str(), value.timestamp); + printf("%s =>\n %s, %lu, %s\n", luamap.c_str(), value.value.c_str(), value.timestamp, value.source.c_str()); + if(value.value != "hello world" || value.timestamp != now || value.source != "test app") + return 1; return 0; } diff --git a/server/src/luaquerymapper.h b/server/src/luaquerymapper.h index 4648606..e8a25f8 100644 --- a/server/src/luaquerymapper.h +++ b/server/src/luaquerymapper.h @@ -33,7 +33,9 @@ #include // For class Value -#include "database.h" +#include "dbtypes.h" + +#include "exception.h" /** * The LUAQueryMapper class takes the result of an external data query and @@ -41,23 +43,39 @@ */ class LUAQueryMapper { public: - LUAQueryMapper(); + /** + * Constructor. + * Initialises the LUA library, and opens a LUA instance. + * Throws an exception if any of these things fail. + */ + LUAQueryMapper() throw(Exception); ~LUAQueryMapper(); /** * Applies the mapping program to the result-namespace, and returns the - * resulting value. + * resulting value. The map must return 3 values; value (a string), timestamp + * (an integer) and the source (string). + * If the program fails to load, fails to run, returns wrong number of values, + * or the wrong types, it will thorw an exception. + * @param mapper A std::string containing the mapping program. + * @return A Value object containing the three result values. */ - Value map(const std::string &mapper); - - void error(std::string message); - - void addQueryResult(QueryResult &result); + Value map(const std::string &mapper) throw(Exception); + + /** + * Add a result to be included in the mapper namespace. + * If in some way the generated values fail (illegal names for example) an exception + * will be thrown. + * @param result The QueryResult object containing the values to add to the + * namespace. + */ + void addQueryResult(QueryResult &result) throw(Exception); private: + void error(std::string message) throw(Exception); + lua_State *L; int clean_top; - }; diff --git a/server/src/queryparser.cc b/server/src/queryparser.cc index 2b0dbdc..602ba80 100644 --- a/server/src/queryparser.cc +++ b/server/src/queryparser.cc @@ -76,12 +76,50 @@ void QueryParser::parseError(char *buf, size_t len, std::string error, int linen if(fwrite(buf, len, 1, stderr) != len) {} fprintf(stderr, "]\n"); fflush(stderr); + + char *slineno; + if(asprintf(&slineno, " at line %d\n", lineno) != -1) { + throw Exception(error + slineno); + free(slineno); + } + } #ifdef TEST_QUERYPARSER -#include "queryhandlerpentominos.h" -#include "tcpsocket.h" +#include + +static char xml[] = + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n" +; + +static char badxml[] = + "\n" + "\n" + "\n" +; static std::string loadresultstring(QueryResult &res, std::string group = "") { @@ -105,20 +143,26 @@ static std::string loadresultstring(QueryResult &res, std::string group = "") int main() { - TCPSocket s; - s.connect("localhost", 11108); - - QueryHandlerPentominos qh(&s, "2003791613"); - - Query q1; - q1.attributes["device_id"] = "lensmeter"; - q1.attributes["device_type"] = "lensmeter"; + // Parse something + { + QueryParser parser; + parser.parse(xml, strlen(xml)); + printf("%s\n", loadresultstring(parser.result).c_str()); + } - QueryResult res = qh.exec(q1); - - printf("%s\n", loadresultstring(res).c_str()); + // Parse something, and fail! + try { + QueryParser parser; + parser.parse(badxml, strlen(badxml)); + printf("%s\n", loadresultstring(parser.result).c_str()); + } catch(Exception &e) { + printf("ERROR: %s\n", e.what()); + goto weitergehen; + } + return 1; + weitergehen: return 0; } -#endif +#endif/*TEST_QUERYPARSER*/ diff --git a/server/src/queryparser.h b/server/src/queryparser.h index 947767c..3a73aa2 100644 --- a/server/src/queryparser.h +++ b/server/src/queryparser.h @@ -27,27 +27,47 @@ #ifndef __PRACRO_QUERYPARSER_H__ #define __PRACRO_QUERYPARSER_H__ -#include - -#include "queryresult.h" #include "saxparser.h" +#include #include +#include "queryresult.h" #include "utf8.h" +#include "exception.h" /** - * This class parses xml entyties into a QueryResult structure. + * This class parses xml entities into a QueryResult structure. + * Call the parent (SAXParser) method parse in order to actually parse something. + * If the parser fails (syntax error) it will throw an Exception. + * @see QueryResult result, in order to get the parsed data. */ class QueryParser : public SAXParser { public: + /** + * Constructor. + */ QueryParser(); + /** + * The object will contain the result when the parsing is done. + */ + QueryResult result; + + /** + * Private parser callback. + */ void startTag(std::string name, std::map< std::string, std::string> attributes); + + /** + * Private parser callback. + */ void endTag(std::string name); - void parseError(char *buf, size_t len, std::string error, int lineno); - QueryResult result; + /** + * Private parser callback. + */ + void parseError(char *buf, size_t len, std::string error, int lineno); private: // For read -- cgit v1.2.3