summaryrefslogtreecommitdiffstats
path: root/src/lib-lua/test-lua.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib-lua/test-lua.c')
-rw-r--r--src/lib-lua/test-lua.c473
1 files changed, 473 insertions, 0 deletions
diff --git a/src/lib-lua/test-lua.c b/src/lib-lua/test-lua.c
new file mode 100644
index 0000000..ff5abe8
--- /dev/null
+++ b/src/lib-lua/test-lua.c
@@ -0,0 +1,473 @@
+/* Copyright (c) 2018 Dovecot authors, see the included COPYING file */
+
+#include "test-lib.h"
+#include "dlua-script-private.h"
+
+#include <math.h>
+
+static int dlua_test_assert(lua_State *L)
+{
+ struct dlua_script *script = dlua_script_from_state(L);
+ const char *what = luaL_checkstring(script->L, 1);
+ bool cond = lua_toboolean(script->L, 2);
+
+ if (!cond) {
+ lua_Debug ar;
+ i_assert(lua_getinfo(L, ">Sl", &ar) == 0);
+ test_assert_failed(what, ar.source, ar.currentline);
+ }
+
+ return 0;
+}
+
+#define GENERATE_GETTERS(name, ctype) \
+static void check_table_get_##name##_ok(struct dlua_script *script, \
+ int idx, ctype expected_value, \
+ const char *str_key, \
+ lua_Integer int_key) \
+{ \
+ ctype value; \
+ int ret; \
+ \
+ /* check string key */ \
+ ret = dlua_table_get_##name##_by_str(script->L, idx, \
+ str_key, &value); \
+ test_assert(ret == 1); \
+ test_assert(value == expected_value); \
+ \
+ /* check int key */ \
+ ret = dlua_table_get_##name##_by_int(script->L, idx, \
+ int_key, &value); \
+ test_assert(ret == 1); \
+ test_assert(value == expected_value); \
+} \
+static void check_table_get_##name##_err(struct dlua_script *script, \
+ int idx, int expected_ret, \
+ const char *str_key, \
+ lua_Integer int_key) \
+{ \
+ ctype value; \
+ int ret; \
+ \
+ /* check string key */ \
+ ret = dlua_table_get_##name##_by_str(script->L, idx, \
+ str_key, &value); \
+ test_assert(ret == expected_ret); \
+ \
+ /* check int key */ \
+ ret = dlua_table_get_##name##_by_int(script->L, idx, \
+ int_key, &value); \
+ test_assert(ret == expected_ret); \
+}
+
+GENERATE_GETTERS(luainteger, lua_Integer);
+GENERATE_GETTERS(int, int);
+GENERATE_GETTERS(intmax, intmax_t);
+GENERATE_GETTERS(uint, unsigned int);
+GENERATE_GETTERS(uintmax, uintmax_t);
+GENERATE_GETTERS(number, lua_Number);
+GENERATE_GETTERS(bool, bool);
+
+/* the string comparison requires us to open-code this */
+static void check_table_get_string_ok(struct dlua_script *script,
+ int idx, const char *expected_value,
+ const char *str_key,
+ lua_Integer int_key)
+{
+ const char *value;
+ int ret;
+
+ /* check string key */
+ ret = dlua_table_get_string_by_str(script->L, idx,
+ str_key, &value);
+ test_assert(ret == 1);
+ test_assert_strcmp(value, expected_value);
+
+ /* check int key */
+ ret = dlua_table_get_string_by_int(script->L, idx,
+ int_key, &value);
+ test_assert(ret == 1);
+ test_assert_strcmp(value, expected_value);
+
+ /* TODO: check thread key, which is difficult */
+}
+
+/* the string comparison of the _ok function requires us to open-code this */
+static void check_table_get_string_err(struct dlua_script *script,
+ int idx, int expected_ret,
+ const char *str_key,
+ lua_Integer int_key)
+{
+ const char *value;
+ int ret;
+
+ /* check string key */
+ ret = dlua_table_get_string_by_str(script->L, idx,
+ str_key, &value);
+ test_assert(ret == expected_ret);
+
+ /* check int key */
+ ret = dlua_table_get_string_by_int(script->L, idx,
+ int_key, &value);
+ test_assert(ret == expected_ret);
+
+ /* TODO: check thread key, which is difficult */
+}
+
+static void check_table_missing(struct dlua_script *script, int idx,
+ const char *str_key,
+ lua_Integer int_key)
+{
+ check_table_get_luainteger_err(script, idx, 0, str_key, int_key);
+ check_table_get_int_err(script, idx, 0, str_key, int_key);
+ check_table_get_intmax_err(script, idx, 0, str_key, int_key);
+ check_table_get_uint_err(script, idx, 0, str_key, int_key);
+ check_table_get_uintmax_err(script, idx, 0, str_key, int_key);
+ check_table_get_number_err(script, idx, 0, str_key, int_key);
+ check_table_get_bool_err(script, idx, 0, str_key, int_key);
+ check_table_get_string_err(script, idx, 0, str_key, int_key);
+}
+
+static void test_lua(void)
+{
+ static const char *luascript =
+"function script_init(req)\n"
+" dovecot.i_debug(\"lua script init called\")\n"
+" local e = dovecot.event()\n"
+" e:log_debug(\"lua script init called from event\")\n"
+" return 0\n"
+"end\n"
+"function lua_function()\n"
+"end\n"
+"function lua_test_flags()\n"
+" local flag = 0\n"
+" flag = dovecot.set_flag(flag, 2)\n"
+" flag = dovecot.set_flag(flag, 4)\n"
+" flag = dovecot.set_flag(flag, 16)\n"
+" test_assert(\"has_flag(flag, 8) == false\", dovecot.has_flag(flag, 8) == false)\n"
+" test_assert(\"has_flag(flag, 4) == true\", dovecot.has_flag(flag, 4) == true)\n"
+" flag = dovecot.clear_flag(flag, 4)\n"
+" test_assert(\"has_flag(flag, 4) == false\", dovecot.has_flag(flag, 4) == false)\n"
+" test_assert(\"has_flag(flag, 16) == true\", dovecot.has_flag(flag, 16) == true)\n"
+"end\n"
+"function lua_test_get_table()\n"
+" t = {}\n"
+" -- zero\n"
+" t[\"zero\"] = 0\n"
+" t[-2] = 0\n"
+" -- small positive values\n"
+" t[\"small-positive-int\"] = 1\n"
+" t[-1] = 1\n"
+" -- small negative values\n"
+" t[\"small-negative-int\"] = -5\n"
+" t[0] = -5\n"
+" -- large positive float\n"
+" t[\"large-positive-int\"] = 2^48\n"
+" t[1] = 2^48\n"
+" -- large negative float\n"
+" t[\"large-negative-int\"] = -2^48\n"
+" t[2] = -2^48\n"
+" -- small float\n"
+" t[\"small-float\"] = 1.525\n"
+" t[3] = 1.525\n"
+" -- bool: true\n"
+" t[\"bool-true\"] = true\n"
+" t[4] = true\n"
+" -- bool: false\n"
+" t[\"bool-false\"] = false\n"
+" t[5] = false\n"
+" -- string\n"
+" t[\"str\"] = \"string\"\n"
+" t[6] = \"string\"\n"
+" return t\n"
+"end\n";
+
+ const char *error = NULL;
+ struct dlua_script *script = NULL;
+
+ test_begin("lua script");
+
+ test_assert(dlua_script_create_string(luascript, &script, NULL, &error) == 0);
+ if (error != NULL)
+ i_fatal("dlua_script_init failed: %s", error);
+
+ dlua_dovecot_register(script);
+
+ dlua_register(script, "test_assert", dlua_test_assert);
+
+ test_assert(dlua_script_init(script, &error) == 0);
+ test_assert(dlua_script_has_function(script, "lua_function"));
+
+ test_assert(dlua_pcall(script->L, "lua_test_flags", 0, 0, &error) == 0);
+
+ lua_getglobal(script->L, "lua_test_get_table");
+ test_assert(lua_pcall(script->L, 0, 1, 0) == 0);
+
+ /*
+ * Check table getters
+ */
+
+ /* lua_Integer */
+ check_table_get_luainteger_ok(script, -1, 0, "zero", -2);
+ check_table_get_luainteger_ok(script, -1, 1, "small-positive-int", -1);
+ check_table_get_luainteger_ok(script, -1, -5, "small-negative-int", 0);
+ check_table_get_luainteger_ok(script, -1, 1ll<<48, "large-positive-int", 1);
+ check_table_get_luainteger_ok(script, -1, -(1ll<<48), "large-negative-int", 2);
+ check_table_get_luainteger_err(script, -1, -1, "small-float", 3);
+ check_table_get_luainteger_err(script, -1, -1, "bool-true", 4);
+ check_table_get_luainteger_err(script, -1, -1, "bool-false", 5);
+ check_table_get_luainteger_err(script, -1, -1, "str", 6);
+
+ /* int */
+ check_table_get_int_ok(script, -1, 0, "zero", -2);
+ check_table_get_int_ok(script, -1, 1, "small-positive-int", -1);
+ check_table_get_int_ok(script, -1, -5, "small-negative-int", 0);
+ check_table_get_int_err(script, -1, -1, "large-positive-int", 1);
+ check_table_get_int_err(script, -1, -1, "large-negative-int", 2);
+ check_table_get_int_err(script, -1, -1, "small-float", 3);
+ check_table_get_int_err(script, -1, -1, "bool-true", 4);
+ check_table_get_int_err(script, -1, -1, "bool-false", 5);
+ check_table_get_int_err(script, -1, -1, "str", 6);
+
+ /* intmax_t */
+ check_table_get_intmax_ok(script, -1, 0, "zero", -2);
+ check_table_get_intmax_ok(script, -1, 1, "small-positive-int", -1);
+ check_table_get_intmax_ok(script, -1, -5, "small-negative-int", 0);
+ check_table_get_intmax_ok(script, -1, 1ll<<48, "large-positive-int", 1);
+ check_table_get_intmax_ok(script, -1, -(1ll<<48), "large-negative-int", 2);
+ check_table_get_intmax_err(script, -1, -1, "small-float", 3);
+ check_table_get_intmax_err(script, -1, -1, "bool-true", 4);
+ check_table_get_intmax_err(script, -1, -1, "bool-false", 5);
+ check_table_get_intmax_err(script, -1, -1, "str", 6);
+
+ /* unsigned int */
+ check_table_get_uint_ok(script, -1, 0, "zero", -2);
+ check_table_get_uint_ok(script, -1, 1, "small-positive-int", -1);
+ check_table_get_uint_err(script, -1, -1, "small-negative-int", 0);
+ check_table_get_uint_err(script, -1, -1, "large-positive-int", 1);
+ check_table_get_uint_err(script, -1, -1, "large-negative-int", 2);
+ check_table_get_uint_err(script, -1, -1, "small-float", 3);
+ check_table_get_uint_err(script, -1, -1, "bool-true", 4);
+ check_table_get_uint_err(script, -1, -1, "bool-false", 5);
+ check_table_get_uint_err(script, -1, -1, "str", 6);
+
+ /* uintmax_t */
+ check_table_get_uintmax_ok(script, -1, 0, "zero", -2);
+ check_table_get_uintmax_ok(script, -1, 1, "small-positive-int", -1);
+ check_table_get_uintmax_err(script, -1, -1, "small-negative-int", 0);
+ check_table_get_uintmax_ok(script, -1, 1ll<<48, "large-positive-int", 1);
+ check_table_get_uintmax_err(script, -1, -1, "large-negative-int", 2);
+ check_table_get_uintmax_err(script, -1, -1, "small-float", 3);
+ check_table_get_uintmax_err(script, -1, -1, "bool-true", 4);
+ check_table_get_uintmax_err(script, -1, -1, "bool-false", 5);
+ check_table_get_uintmax_err(script, -1, -1, "str", 6);
+
+ /* lua_Number */
+ check_table_get_number_ok(script, -1, 0, "zero", -2);
+ check_table_get_number_ok(script, -1, 1, "small-positive-int", -1);
+ check_table_get_number_ok(script, -1, -5, "small-negative-int", 0);
+ check_table_get_number_ok(script, -1, 1ll<<48, "large-positive-int", 1);
+ check_table_get_number_ok(script, -1, -(1ll<<48), "large-negative-int", 2);
+ check_table_get_number_ok(script, -1, 1.525, "small-float", 3);
+ check_table_get_number_err(script, -1, -1, "bool-true", 4);
+ check_table_get_number_err(script, -1, -1, "bool-false", 5);
+ check_table_get_number_err(script, -1, -1, "str", 6);
+
+ /* bool */
+ check_table_get_bool_err(script, -1, -1, "zero", -2);
+ check_table_get_bool_err(script, -1, -1, "small-positive-int", -1);
+ check_table_get_bool_err(script, -1, -1, "small-negative-int", 0);
+ check_table_get_bool_err(script, -1, -1, "large-positive-int", 1);
+ check_table_get_bool_err(script, -1, -1, "large-negative-int", 2);
+ check_table_get_bool_err(script, -1, -1, "small-float", 3);
+ check_table_get_bool_ok(script, -1, TRUE, "bool-true", 4);
+ check_table_get_bool_ok(script, -1, FALSE, "bool-false", 5);
+ check_table_get_bool_err(script, -1, -1, "str", 6);
+
+ /* const char * */
+ check_table_get_string_err(script, -1, -1, "zero", -2);
+ check_table_get_string_err(script, -1, -1, "small-positive-int", -1);
+ check_table_get_string_err(script, -1, -1, "small-negative-int", 0);
+ check_table_get_string_err(script, -1, -1, "large-positive-int", 1);
+ check_table_get_string_err(script, -1, -1, "large-negative-int", 2);
+ check_table_get_string_err(script, -1, -1, "small-float", 3);
+ check_table_get_string_err(script, -1, -1, "bool-true", 4);
+ check_table_get_string_err(script, -1, -1, "bool-false", 5);
+ check_table_get_string_ok(script, -1, "string", "str", 6);
+
+ check_table_missing(script, -1, "missing", -10);
+
+ lua_pop(script->L, 1);
+
+ dlua_script_unref(&script);
+
+ test_end();
+}
+
+static void test_tls(void)
+{
+ const char *error = NULL;
+ struct dlua_script *script = NULL;
+ lua_State *L1, *L2;
+
+ test_begin("lua thread local storage");
+
+ test_assert(dlua_script_create_string("", &script, NULL, &error) == 0);
+ if (error != NULL)
+ i_fatal("dlua_script_init failed: %s", error);
+
+ L1 = dlua_script_new_thread(script);
+ L2 = dlua_script_new_thread(script);
+
+ dlua_tls_set_ptr(L1, "ptr", L1);
+ test_assert(dlua_tls_get_ptr(L1, "ptr") == L1);
+ test_assert(dlua_tls_get_ptr(L2, "ptr") == NULL);
+ test_assert(dlua_tls_get_int(L1, "int") == 0);
+ test_assert(dlua_tls_get_int(L2, "int") == 0);
+
+ dlua_tls_set_ptr(L2, "ptr", L2);
+ test_assert(dlua_tls_get_ptr(L1, "ptr") == L1);
+ test_assert(dlua_tls_get_ptr(L2, "ptr") == L2);
+ test_assert(dlua_tls_get_int(L1, "int") == 0);
+ test_assert(dlua_tls_get_int(L2, "int") == 0);
+
+ dlua_tls_set_int(L1, "int", 1);
+ dlua_tls_set_int(L2, "int", 2);
+ test_assert(dlua_tls_get_int(L1, "int") == 1);
+ test_assert(dlua_tls_get_int(L2, "int") == 2);
+
+ dlua_tls_clear(L1, "ptr");
+ test_assert(dlua_tls_get_ptr(L1, "ptr") == NULL);
+ test_assert(dlua_tls_get_ptr(L2, "ptr") == L2);
+ test_assert(dlua_tls_get_int(L1, "int") == 1);
+ test_assert(dlua_tls_get_int(L2, "int") == 2);
+
+ dlua_script_close_thread(script, &L1);
+
+ test_assert(dlua_tls_get_ptr(L2, "ptr") == L2);
+ test_assert(dlua_tls_get_int(L2, "int") == 2);
+
+ dlua_tls_clear(L2, "ptr");
+ dlua_script_close_thread(script, &L2);
+
+ dlua_script_unref(&script);
+
+ test_end();
+}
+
+/* check lua_tointegerx against top-of-stack item */
+static void check_tointegerx_compat(lua_State *L, bool expected_isnum,
+ bool expected_isint,
+ lua_Integer expected_value)
+{
+ lua_Integer value;
+ int isnum;
+
+ value = lua_tointegerx(L, -1, &isnum);
+ test_assert((isnum == 1) == expected_isnum);
+
+ if (isnum == 1)
+ test_assert(value == expected_value);
+
+ test_assert(lua_isinteger(L, -1) == expected_isint);
+
+ lua_pop(L, 1);
+}
+
+static void test_compat_tointegerx_and_isinteger(void)
+{
+ static const struct {
+ const char *input;
+ lua_Integer output;
+ bool isnum;
+ } str_tests[] = {
+ { "-1", -1, TRUE },
+ { "0", 0, TRUE },
+ { "1", 1, TRUE },
+ { "-2147483648", -2147483648, TRUE },
+ { "2147483647", 2147483647, TRUE },
+ { "0x123", 0x123, TRUE },
+ { "0123", 123, TRUE }, /* NB: lua doesn't use leading zero for octal */
+ { "0xabcdef", 0xabcdef, TRUE },
+ { "0xabcdefg", 0, FALSE },
+ { "abc", 0, FALSE },
+ { "1.525", 0, FALSE },
+ { "52.51", 0, FALSE },
+ };
+ static const struct {
+ lua_Number input;
+ lua_Integer output;
+ bool isnum;
+ } num_tests[] = {
+ { -1, -1, TRUE },
+ { 0, 0, TRUE },
+ { 1, 1, TRUE },
+ { INT_MIN, INT_MIN, TRUE },
+ { INT_MAX, INT_MAX, TRUE },
+ { 1.525, 0, FALSE },
+ { 52.51, 0, FALSE },
+ { NAN, 0, FALSE },
+ { +INFINITY, 0, FALSE },
+ { -INFINITY, 0, FALSE },
+ };
+ static const struct {
+ lua_Integer input;
+ lua_Integer output;
+ } int_tests[] = {
+ { -1, -1 },
+ { 0, 0 },
+ { 1, 1 },
+ { INT_MIN, INT_MIN },
+ { INT_MAX, INT_MAX },
+ };
+ struct dlua_script *script;
+ const char *error;
+ size_t i;
+
+ test_begin("lua compat tostringx/isinteger");
+
+ test_assert(dlua_script_create_string("", &script, NULL, &error) == 0);
+
+ for (i = 0; i < N_ELEMENTS(str_tests); i++) {
+ lua_pushstring(script->L, str_tests[i].input);
+ check_tointegerx_compat(script->L, str_tests[i].isnum, FALSE,
+ str_tests[i].output);
+ }
+
+ for (i = 0; i < N_ELEMENTS(num_tests); i++) {
+ bool isint;
+
+ /* See lua_isinteger() comment in dlua-compat.h */
+#if LUA_VERSION_NUM >= 503
+ isint = FALSE;
+#else
+ isint = num_tests[i].isnum;
+#endif
+
+ lua_pushnumber(script->L, num_tests[i].input);
+ check_tointegerx_compat(script->L, num_tests[i].isnum,
+ isint,
+ num_tests[i].output);
+ }
+
+ for (i = 0; i < N_ELEMENTS(int_tests); i++) {
+ lua_pushinteger(script->L, int_tests[i].input);
+ check_tointegerx_compat(script->L, TRUE, TRUE,
+ int_tests[i].output);
+ }
+
+ dlua_script_unref(&script);
+
+ test_end();
+}
+
+int main(void) {
+ void (*tests[])(void) = {
+ test_lua,
+ test_tls,
+ test_compat_tointegerx_and_isinteger,
+ NULL
+ };
+
+ return test_run(tests);
+}