// testLua.c -- tests for the Lua 5.4 adapter against the broker core. // // Exercises: a broker native called from Lua, scalar/table marshalling in both // directions (binary-safe strings included), a Lua function exported and invoked // from C, and a function value pushed into Lua and called there. #include "calogInternal.h" #include "luaAdapter.h" #include #include #include #define CHECK(cond, msg) checkImpl((cond), (msg), __FILE__, __LINE__) #define LIST_ELEM_BASE 10 #define LIST_ELEMS 3 static CalogT *broker = NULL; static CalogLuaT *lua = NULL; static CalogValueT recorded; static int32_t testsRun = 0; static int32_t testsFailed = 0; static void checkImpl(bool condition, const char *message, const char *file, int32_t line); static int32_t nativeAdd(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData); static int32_t nativeGetAdder(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData); static int32_t nativeMakeList(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData); static int32_t nativeRecord(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData); static void testBinaryString(void); static void testExportInvoke(void); static void testFunctionValueIntoLua(void); static void testNativeFromLua(void); static void testTableFromNative(void); static void testTableToNative(void); static void checkImpl(bool condition, const char *message, const char *file, int32_t line) { testsRun++; if (!condition) { testsFailed++; printf("FAIL %s:%d %s\n", file, line, message); } } static int32_t nativeAdd(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData) { (void)userData; if (argCount != 2 || args[0].type != calogIntE || args[1].type != calogIntE) { return calogFail(result, calogErrArgE, "add expects two integers"); } calogValueInt(result, args[0].as.i + args[1].as.i); return calogOkE; } static int32_t nativeGetAdder(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData) { CalogFnT *callable; int32_t status; (void)args; (void)argCount; (void)userData; status = calogFnCreate(&callable, broker, nativeAdd, NULL, NULL, 0); if (status != calogOkE) { return calogFail(result, status, "getAdder out of memory"); } calogValueFn(result, callable); return calogOkE; } static int32_t nativeMakeList(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData) { CalogAggT *aggregate; CalogValueT element; int32_t status; int32_t index; (void)args; (void)argCount; (void)userData; status = calogAggCreate(&aggregate, calogListE); if (status != calogOkE) { return calogFail(result, status, "makeList out of memory"); } for (index = 0; index < LIST_ELEMS; index++) { calogValueInt(&element, (int64_t)(index + 1) * LIST_ELEM_BASE); status = calogAggPush(aggregate, &element); if (status != calogOkE) { calogValueFree(&element); calogAggFree(aggregate); return calogFail(result, status, "makeList push failed"); } } calogValueAgg(result, aggregate); return calogOkE; } static int32_t nativeRecord(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData) { int32_t status; (void)userData; calogValueNil(result); calogValueFree(&recorded); if (argCount < 1) { return calogOkE; } status = calogValueCopy(&recorded, &args[0]); if (status != calogOkE) { return calogFail(result, status, "record copy failed"); } return calogOkE; } static void testBinaryString(void) { int32_t status; status = calogLuaRun(lua, "record('a\\0b')"); CHECK(status == calogOkE, "lua binary string run"); CHECK(recorded.type == calogStringE && recorded.as.s.length == 3, "binary string length preserved"); CHECK(recorded.as.s.bytes[0] == 'a' && recorded.as.s.bytes[1] == '\0' && recorded.as.s.bytes[2] == 'b', "embedded NUL survived marshalling"); } static void testExportInvoke(void) { CalogFnT *doubler; CalogValueT arg; CalogValueT result; int32_t status; status = calogLuaRun(lua, "function doubler(x) return x * 2 end"); CHECK(status == calogOkE, "define doubler"); status = calogLuaExport(lua, "doubler", &doubler); CHECK(status == calogOkE, "export doubler"); calogValueInt(&arg, 21); status = calogFnInvoke(doubler, &arg, 1, &result); CHECK(status == calogOkE, "invoke exported Lua function from C"); CHECK(result.type == calogIntE && result.as.i == 42, "exported Lua function result"); calogValueFree(&result); calogValueFree(&arg); calogFnRelease(doubler); } static void testFunctionValueIntoLua(void) { int32_t status; status = calogLuaRun(lua, "record(getAdder()(2, 3))"); CHECK(status == calogOkE, "function value into Lua run"); CHECK(recorded.type == calogIntE && recorded.as.i == 5, "function value called inside Lua"); } static void testNativeFromLua(void) { int32_t status; status = calogLuaRun(lua, "record(add(3, 4))"); CHECK(status == calogOkE, "native from Lua run"); CHECK(recorded.type == calogIntE && recorded.as.i == 7, "native add result recorded"); } static void testTableFromNative(void) { int32_t status; status = calogLuaRun(lua, "local t = makeList(); record(t[1] + t[2] + t[3])"); CHECK(status == calogOkE, "table from native run"); CHECK(recorded.type == calogIntE && recorded.as.i == 60, "aggregate marshalled to Lua table (values)"); status = calogLuaRun(lua, "record(#makeList())"); CHECK(status == calogOkE, "table length run"); CHECK(recorded.type == calogIntE && recorded.as.i == LIST_ELEMS, "aggregate marshalled to Lua table (length)"); } static void testTableToNative(void) { CalogValueT key; CalogValueT *found; int32_t status; status = calogLuaRun(lua, "record({10, 20, x = true})"); CHECK(status == calogOkE, "table to native run"); CHECK(recorded.type == calogAggE, "Lua table marshalled to aggregate"); CHECK(recorded.as.agg->arrayCount == 2, "table array part count"); CHECK(recorded.as.agg->array[0].as.i == 10 && recorded.as.agg->array[1].as.i == 20, "table array part values"); CHECK(recorded.as.agg->pairCount == 1, "table hash part count"); calogValueString(&key, "x", 1); found = calogAggGet(recorded.as.agg, &key); calogValueFree(&key); CHECK(found != NULL && found->type == calogBoolE && found->as.b == true, "table hash part value"); } int main(void) { int32_t status; calogValueNil(&recorded); broker = calogBrokerCreate(); if (broker == NULL) { printf("broker create failed\n"); return 1; } calogRegister(broker, "add", nativeAdd, NULL); calogRegister(broker, "record", nativeRecord, NULL); calogRegister(broker, "makeList", nativeMakeList, NULL); calogRegister(broker, "getAdder", nativeGetAdder, NULL); status = calogLuaCreate(&lua, broker, 1); if (status != calogOkE) { printf("lua context create failed\n"); return 1; } calogLuaExpose(lua, "add"); calogLuaExpose(lua, "record"); calogLuaExpose(lua, "makeList"); calogLuaExpose(lua, "getAdder"); testNativeFromLua(); testTableToNative(); testTableFromNative(); testExportInvoke(); testFunctionValueIntoLua(); testBinaryString(); calogValueFree(&recorded); calogLuaDestroy(lua); calogBrokerDestroy(broker); printf("\n%d checks, %d failed\n", testsRun, testsFailed); if (testsFailed != 0) { return 1; } return 0; }