From: Vsevolod Stakhov Date: Fri, 5 Dec 2025 20:43:20 +0000 (+0000) Subject: [Test] Fix Lua 5.4 and cffi-lua compatibility issues X-Git-Tag: 3.14.2~22 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=23ed5d4ba52479c569193236e1500ba27d8bd580;p=thirdparty%2Frspamd.git [Test] Fix Lua 5.4 and cffi-lua compatibility issues - Fix rspamd_memspn to handle empty character set without crash - Add unpack compatibility shim for Lua 5.2+ (table.unpack) - Replace deprecated table.maxn with # operator - Fix cffi-lua strict type checking (char* vs unsigned char*) - Add helpers for cdata-to-number conversion (64-bit integers) - Add proper NULL pointer detection for cffi-lua - Fix lua_resume to use coroutine threads in Lua 5.4 - Update test expectations for Lua 5.4 tostring(float) behavior --- diff --git a/src/libutil/str_util.c b/src/libutil/str_util.c index eaaeaccfe0..8ddd4f4a3a 100644 --- a/src/libutil/str_util.c +++ b/src/libutil/str_util.c @@ -2963,7 +2963,13 @@ gsize rspamd_memspn(const char *s, const char *e, gsize len) gsize byteset[32 / sizeof(gsize)]; const char *p = s, *end = s + len; + if (!e[0]) { + /* Empty set - nothing matches */ + return 0; + } + if (!e[1]) { + /* Single character optimization */ for (; p < end && *p == *e; p++); return p - s; } diff --git a/test/lua/telescope.lua b/test/lua/telescope.lua index e2d9e65fd9..29d9f36c49 100644 --- a/test/lua/telescope.lua +++ b/test/lua/telescope.lua @@ -36,6 +36,9 @@ local compat_env = require 'compat_env' local getfenv = _G.getfenv or compat_env.getfenv local setfenv = _G.setfenv or compat_env.setfenv +-- Lua 5.2+ compatibility: unpack was moved to table.unpack +local unpack = _G.unpack or table.unpack + local _VERSION = "0.6.0" diff --git a/test/lua/unit/regxep.lua b/test/lua/unit/regxep.lua index a27e7b3639..25bf09c42b 100644 --- a/test/lua/unit/regxep.lua +++ b/test/lua/unit/regxep.lua @@ -1,6 +1,6 @@ context("Regexp unit tests", function() local re = require("rspamd_regexp") - + test("Regexp creation", function() assert_not_nil(re.create_cached('/test$/m')) assert_not_nil(re.create_cached('^test$', 'm')) @@ -21,36 +21,36 @@ context("Regexp unit tests", function() -- Cyrillic utf8 letter {'/\\S<[-\\w\\.]+\\@[-\\w\\.]+>/r', 'some', false}, } - + for _,c in ipairs(cases) do local r = re.create_cached(c[1]) assert_not_nil(r, "cannot parse " .. c[1]) local res = r:match(c[2]) - + assert_equal(res, c[3], string.format("'%s' doesn't match with '%s'", c[2], c[1])) end end) - + test("Regexp capture", function() local cases = { - {'Body=(\\S+)(?: Fuz1=(\\S+))?(?: Fuz2=(\\S+))?', - 'mc-filter4 1120; Body=1 Fuz1=2 Fuz2=3', + {'Body=(\\S+)(?: Fuz1=(\\S+))?(?: Fuz2=(\\S+))?', + 'mc-filter4 1120; Body=1 Fuz1=2 Fuz2=3', {'Body=1 Fuz1=2 Fuz2=3', '1', '2', '3'}}, - {'Body=(\\S+)(?: Fuz1=(\\S+))?(?: Fuz2=(\\S+))?', + {'Body=(\\S+)(?: Fuz1=(\\S+))?(?: Fuz2=(\\S+))?', 'mc-filter4 1120; Body=1 Fuz1=2', {'Body=1 Fuz1=2', '1', '2'}}, - {'Body=(\\S+)(?: Fuz1=(\\S+))?(?: Fuz2=(\\S+))?', - 'mc-filter4 1120; Body=1 Fuz1=2 mc-filter4 1120; Body=1 Fuz1=2 Fuz2=3', + {'Body=(\\S+)(?: Fuz1=(\\S+))?(?: Fuz2=(\\S+))?', + 'mc-filter4 1120; Body=1 Fuz1=2 mc-filter4 1120; Body=1 Fuz1=2 Fuz2=3', {'Body=1 Fuz1=2', '1', '2'}, {'Body=1 Fuz1=2 Fuz2=3', '1', '2', '3'}}, } for _,c in ipairs(cases) do local r = re.create_cached(c[1]) assert_not_nil(r, "cannot parse " .. c[1]) local res = r:search(c[2], false, true) - + assert_not_nil(res, "cannot find pattern") - - for k = 3, table.maxn(c) do + + for k = 3, #c do for n,m in ipairs(c[k]) do assert_equal(res[k - 2][n], c[k][n], string.format("'%s' doesn't match with '%s'", c[k][n], res[k - 2][n])) @@ -58,7 +58,7 @@ context("Regexp unit tests", function() end end end) - + test("Regexp split", function() local cases = { {'\\s', 'one', {'one'}}, -- one arg @@ -68,23 +68,23 @@ context("Regexp unit tests", function() {'\\s', ' one two ', {'one', 'two'}}, -- multiple delimiters {'\\s', ' one ', {'one'}}, -- multiple delimiters {'[:,]', ',,,:::one,two,,', {'one', 'two'}}, -- multiple delimiters - {'[\\|\\s]', '16265 | 1.1.1.0/22 | TR | ripencc | 2014-02-28', + {'[\\|\\s]', '16265 | 1.1.1.0/22 | TR | ripencc | 2014-02-28', {'16265', '1.1.1.0/22', 'TR', 'ripencc', '2014-02-28'}}, -- practical {'|', '16265 | 1.1.1.0/22 | TR | ripencc | 2014-02-28', {}} -- bad re } - + for _,c in ipairs(cases) do local r = re.create_cached(c[1]) assert_not_nil(r, "cannot parse " .. c[1]) - + local res = r:split(c[2]) assert_not_nil(res, "cannot split " .. c[2]) - + for i,r in ipairs(c[3]) do assert_equal(res[i], r) end end end) - + end ) \ No newline at end of file diff --git a/test/lua/unit/selectors.lua b/test/lua/unit/selectors.lua index 6362e5cdea..1266a90521 100644 --- a/test/lua/unit/selectors.lua +++ b/test/lua/unit/selectors.lua @@ -230,11 +230,11 @@ context("Selectors test", function() ["pool_var double"] = { selector = [[pool_var("int_var", 'double')]], - expect = {"1"}}, + expect = {"1.0"}}, ["time"] = { selector = "time", - expect = {"1537364211"}}, + expect = {"1537364211.0"}}, -- ["request_header"] = { -- selector = "request_header(hdr1)", diff --git a/test/lua/unit/smtp_addr.lua b/test/lua/unit/smtp_addr.lua index afccd0a750..c4ac167747 100644 --- a/test/lua/unit/smtp_addr.lua +++ b/test/lua/unit/smtp_addr.lua @@ -86,11 +86,18 @@ context("SMTP address check functions", function() '<>', } + -- Helper to check if a cdata pointer is NULL (cffi-lua compatibility) + local function is_null(ptr) + if ptr == nil then return true end + -- For cffi-lua, check if pointer equals NULL + return ptr == ffi.new('void*') + end + fun.each(function(case) test("Parse invalid smtp addr: " .. case, function() local st = ffi.C.rspamd_email_address_from_smtp(case, #case) - assert_nil(st, "should not be able to parse " .. case) + assert_true(is_null(st), "should not be able to parse " .. case) end) end, cases_invalid) diff --git a/test/lua/unit/url.lua b/test/lua/unit/url.lua index d5c3ad42df..83ee0fc476 100644 --- a/test/lua/unit/url.lua +++ b/test/lua/unit/url.lua @@ -215,13 +215,22 @@ context("URL check functions", function() { "///foo", "/foo" }, } + -- Helper to convert cdata numbers to Lua numbers (cffi-lua compatibility) + local function cdata_to_number(v) + -- Try tonumber first (works in LuaJIT and for small values) + local n = tonumber(v) + if n then return n end + -- For 64-bit integers in cffi-lua, convert via string + return tonumber(tostring(v):match("^(%d+)")) + end + for i, v in ipairs(cases) do test(string.format("Normalize paths '%s'", v[1]), function() - local buf = ffi.new("uint8_t[?]", #v[1]) + local buf = ffi.new("char[?]", #v[1]) local sizbuf = ffi.new("size_t[1]") ffi.copy(buf, v[1], #v[1]) ffi.C.rspamd_normalize_path_inplace(buf, #v[1], sizbuf) - local res = ffi.string(buf, tonumber(sizbuf[0])) + local res = ffi.string(buf, cdata_to_number(sizbuf[0])) assert_equal(v[2], res, 'expected ' .. v[2] .. ' but got ' .. res .. ' in path ' .. v[1]) end) end diff --git a/test/lua/unit/utf.lua b/test/lua/unit/utf.lua index a4ea451b56..dbf493e3b9 100644 --- a/test/lua/unit/utf.lua +++ b/test/lua/unit/utf.lua @@ -11,8 +11,8 @@ context("UTF8 check functions", function() void rspamd_fast_utf8_library_init (unsigned flags); void ottery_rand_bytes(void *buf, size_t n); double rspamd_get_ticks(int allow); - size_t rspamd_fast_utf8_validate (const unsigned char *data, size_t len); - size_t rspamd_fast_utf8_validate_ref (const unsigned char *data, size_t len); + size_t rspamd_fast_utf8_validate (const char *data, size_t len); + size_t rspamd_fast_utf8_validate_ref (const char *data, size_t len); char * rspamd_str_make_utf_valid (const char *src, size_t slen, size_t *dstlen, void *); ]] @@ -78,6 +78,15 @@ context("UTF8 check functions", function() end) end + -- Helper to convert cdata numbers to Lua numbers (cffi-lua compatibility) + local function cdata_to_number(v) + -- Try tonumber first (works in LuaJIT and for small values) + local n = tonumber(v) + if n then return n end + -- For 64-bit integers in cffi-lua, convert via string + return tonumber(tostring(v):match("^(%d+)")) + end + -- Enable sse and avx2 ffi.C.rspamd_fast_utf8_library_init(3) local valid_cases = { @@ -93,7 +102,7 @@ context("UTF8 check functions", function() ffi.copy(buf, c) local ret = ffi.C.rspamd_fast_utf8_validate(buf, #c) - assert_equal(ret, 0) + assert_equal(cdata_to_number(ret), 0) end) end local invalid_cases = { @@ -119,7 +128,7 @@ context("UTF8 check functions", function() ffi.copy(buf, c) local ret = ffi.C.rspamd_fast_utf8_validate(buf, #c) - assert_not_equal(ret, 0) + assert_not_equal(cdata_to_number(ret), 0) end) end diff --git a/test/rspamd_lua_pcall_vs_resume_test.c b/test/rspamd_lua_pcall_vs_resume_test.c index 82de9bb8ba..b7930f9b06 100644 --- a/test/rspamd_lua_pcall_vs_resume_test.c +++ b/test/rspamd_lua_pcall_vs_resume_test.c @@ -53,15 +53,19 @@ test_resume(lua_State *L, int function_call) t1 = rspamd_get_virtual_ticks(); for (i = 0; i < N; i++) { - lua_rawgeti(L, LUA_REGISTRYINDEX, function_call); + /* In Lua 5.4, lua_resume must be called on a coroutine, not the main thread */ + lua_State *coro = lua_newthread(L); + lua_rawgeti(coro, LUA_REGISTRYINDEX, function_call); #if LUA_VERSION_NUM < 502 - lua_resume(L, 0); + lua_resume(coro, 0); #elif LUA_VERSION_NUM >= 504 - lua_resume(L, NULL, 0, NULL); + int nres; + lua_resume(coro, NULL, 0, &nres); #else - lua_resume(L, NULL, 0); + lua_resume(coro, NULL, 0); #endif - lua_pop(L, 1); + lua_pop(coro, 1); + lua_pop(L, 1); /* pop the coroutine from main state */ } t2 = rspamd_get_virtual_ticks(); @@ -85,7 +89,8 @@ test_resume_get_thread(int function_call) #if LUA_VERSION_NUM < 502 lua_resume(ent->lua_state, 0); #elif LUA_VERSION_NUM >= 504 - lua_resume(ent->lua_state, NULL, 0, NULL); + int nres; + lua_resume(ent->lua_state, NULL, 0, &nres); #else lua_resume(ent->lua_state, NULL, 0); #endif @@ -115,7 +120,8 @@ test_resume_get_new_thread(int function_call) #if LUA_VERSION_NUM < 502 lua_resume(ent->lua_state, 0); #elif LUA_VERSION_NUM >= 504 - lua_resume(ent->lua_state, NULL, 0, NULL); + int nres; + lua_resume(ent->lua_state, NULL, 0, &nres); #else lua_resume(ent->lua_state, NULL, 0); #endif