diff options
Diffstat (limited to 'src/lib-lua/test-lua.c')
-rw-r--r-- | src/lib-lua/test-lua.c | 473 |
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); +} |