aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2015-02-28 00:11:56 +0000
committerVsevolod Stakhov <vsevolod@highsecure.ru>2015-02-28 00:11:56 +0000
commite4cc017c5d44c1046e6afdfc1e68d756c8748b6b (patch)
tree3e0be3b2b40f6ef509cd4d00f0e4d1166d575209
parentf1752bac0b0c00717478bc6633e4afb527bd46da (diff)
downloadrspamd-e4cc017c5d44c1046e6afdfc1e68d756c8748b6b.tar.gz
rspamd-e4cc017c5d44c1046e6afdfc1e68d756c8748b6b.zip
Rework lua tests one more time.
-rw-r--r--config.h.in2
-rw-r--r--test/CMakeLists.txt12
-rw-r--r--test/lua/compat_env.lua391
-rw-r--r--test/lua/telescope.lua621
-rw-r--r--test/lua/tests.lua22
-rw-r--r--test/lua/unit/rsa.lua (renamed from test/lua/rsa.lua)0
-rw-r--r--test/lua/unit/test.data (renamed from test/lua/test.data)0
-rw-r--r--test/lua/unit/testkey (renamed from test/lua/testkey)0
-rw-r--r--test/lua/unit/testkey.pub (renamed from test/lua/testkey.pub)0
-rw-r--r--test/rspamd_lua_test.c87
-rw-r--r--test/rspamd_test_suite.c35
-rw-r--r--test/tests.h2
12 files changed, 1127 insertions, 45 deletions
diff --git a/config.h.in b/config.h.in
index e45f95f4c..37267d468 100644
--- a/config.h.in
+++ b/config.h.in
@@ -244,6 +244,8 @@
#define MODULES_NUM ${RSPAMD_MODULES_NUM}
+#define BUILDROOT "${CMAKE_BINARY_DIR}"
+
/* sys/types */
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index b4fa018d2..18a826026 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -24,10 +24,18 @@ TARGET_LINK_LIBRARIES(rspamd-test stemmer)
IF(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
# Also add dependencies for convenience
- FILE(GLOB LUA_TESTS "${CMAKE_CURRENT_SOURCE_DIR}/lua/*")
+ FILE(GLOB_RECURSE LUA_TESTS "${CMAKE_CURRENT_SOURCE_DIR}/lua/*")
+ ADD_CUSTOM_TARGET(units-dir COMMAND
+ ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/lua/unit"
+ )
+ ADD_DEPENDENCIES(rspamd-test units-dir)
FOREACH(_LF IN LISTS LUA_TESTS)
GET_FILENAME_COMPONENT(_NM "${_LF}" NAME)
- SET(_DS "${CMAKE_CURRENT_BINARY_DIR}/lua/${_NM}")
+ IF("${_LF}" MATCHES "^.*/unit/.*$")
+ SET(_DS "${CMAKE_CURRENT_BINARY_DIR}/lua/unit/${_NM}")
+ ELSE()
+ SET(_DS "${CMAKE_CURRENT_BINARY_DIR}/lua/${_NM}")
+ ENDIF()
ADD_CUSTOM_TARGET("${_NM}" COMMAND
${CMAKE_COMMAND} -E copy_if_different ${_LF} ${_DS}
SOURCES "${_LF}"
diff --git a/test/lua/compat_env.lua b/test/lua/compat_env.lua
new file mode 100644
index 000000000..2ecd4b6be
--- /dev/null
+++ b/test/lua/compat_env.lua
@@ -0,0 +1,391 @@
+--[[
+
+ compat_env v$(_VERSION) - Lua 5.1/5.2 environment compatibility functions
+
+SYNOPSIS
+
+ -- Get load/loadfile compatibility functions only if using 5.1.
+ local CL = pcall(load, '') and _G or require 'compat_env'
+ local load = CL.load
+ local loadfile = CL.loadfile
+
+ -- The following now works in both Lua 5.1 and 5.2:
+ assert(load('return 2*pi', nil, 't', {pi=math.pi}))()
+ assert(loadfile('ex.lua', 't', {print=print}))()
+
+ -- Get getfenv/setfenv compatibility functions only if using 5.2.
+ local getfenv = _G.getfenv or require 'compat_env'.getfenv
+ local setfenv = _G.setfenv or require 'compat_env'.setfenv
+ local function f() return x end
+ setfenv(f, {x=2})
+ print(x, getfenv(f).x) --> 2, 2
+
+DESCRIPTION
+
+ This module provides Lua 5.1/5.2 environment related compatibility functions.
+ This includes implementations of Lua 5.2 style `load` and `loadfile`
+ for use in Lua 5.1. It also includes Lua 5.1 style `getfenv` and `setfenv`
+ for use in Lua 5.2.
+
+API
+
+ local CL = require 'compat_env'
+
+ CL.load (ld [, source [, mode [, env] ] ]) --> f [, err]
+
+ This behaves the same as the Lua 5.2 `load` in both
+ Lua 5.1 and 5.2.
+ http://www.lua.org/manual/5.2/manual.html#pdf-load
+
+ CL.loadfile ([filename [, mode [, env] ] ]) --> f [, err]
+
+ This behaves the same as the Lua 5.2 `loadfile` in both
+ Lua 5.1 and 5.2.
+ http://www.lua.org/manual/5.2/manual.html#pdf-loadfile
+
+ CL.getfenv ([f]) --> t
+
+ This is identical to the Lua 5.1 `getfenv` in Lua 5.1.
+ This behaves similar to the Lua 5.1 `getfenv` in Lua 5.2.
+ When a global environment is to be returned, or when `f` is a
+ C function, this returns `_G` since Lua 5.2 doesn't have
+ (thread) global and C function environments. This will also
+ return `_G` if the Lua function `f` lacks an `_ENV`
+ upvalue, but it will raise an error if uncertain due to lack of
+ debug info. It is not normally considered good design to use
+ this function; when possible, use `load` or `loadfile` instead.
+ http://www.lua.org/manual/5.1/manual.html#pdf-getfenv
+
+ CL.setfenv (f, t)
+
+ This is identical to the Lua 5.1 `setfenv` in Lua 5.1.
+ This behaves similar to the Lua 5.1 `setfenv` in Lua 5.2.
+ This will do nothing if `f` is a Lua function that
+ lacks an `_ENV` upvalue, but it will raise an error if uncertain
+ due to lack of debug info. See also Design Notes below.
+ It is not normally considered good design to use
+ this function; when possible, use `load` or `loadfile` instead.
+ http://www.lua.org/manual/5.1/manual.html#pdf-setfenv
+
+DESIGN NOTES
+
+ This module intends to provide robust and fairly complete reimplementations
+ of the environment related Lua 5.1 and Lua 5.2 functions.
+ No effort is made, however, to simulate rare or difficult to simulate features,
+ such as thread environments, although this is liable to change in the future.
+ Such 5.1 capabilities are discouraged and ideally
+ removed from 5.1 code, thereby allowing your code to work in both 5.1 and 5.2.
+
+ In Lua 5.2, a `setfenv(f, {})`, where `f` lacks any upvalues, will be silently
+ ignored since there is no `_ENV` in this function to write to, and the
+ environment will have no effect inside the function anyway. However,
+ this does mean that `getfenv(setfenv(f, t))` does not necessarily equal `t`,
+ which is incompatible with 5.1 code (a possible workaround would be [1]).
+ If `setfenv(f, {})` has an upvalue but no debug info, then this will raise
+ an error to prevent inadvertently executing potentially untrusted code in the
+ global environment.
+
+ It is not normally considered good design to use `setfenv` and `getfenv`
+ (one reason they were removed in 5.2). When possible, consider replacing
+ these with `load` or `loadfile`, which are more restrictive and have native
+ implementations in 5.2.
+
+ This module might be merged into a more general Lua 5.1/5.2 compatibility
+ library (e.g. a full reimplementation of Lua 5.2 `_G`). However,
+ `load/loadfile/getfenv/setfenv` perhaps are among the more cumbersome
+ functions not to have.
+
+INSTALLATION
+
+ Download compat_env.lua:
+
+ wget https://raw.github.com/gist/1654007/compat_env.lua
+
+ Copy compat_env.lua into your LUA_PATH.
+
+ Alternately, unpack, test, and install into LuaRocks:
+
+ wget https://raw.github.com/gist/1422205/sourceunpack.lua
+ lua sourceunpack.lua compat_env.lua
+ (cd out && luarocks make)
+
+Related work
+
+ http://lua-users.org/wiki/LuaVersionCompatibility
+ https://github.com/stevedonovan/Penlight/blob/master/lua/pl/utils.lua
+ - penlight implementations of getfenv/setfenv
+ http://lua-users.org/lists/lua-l/2010-06/msg00313.html
+ - initial getfenv/setfenv implementation
+
+References
+
+ [1] http://lua-users.org/lists/lua-l/2010-06/msg00315.html
+
+Copyright
+
+(c) 2012 David Manura. Licensed under the same terms as Lua 5.1/5.2 (MIT license).
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+--]]---------------------------------------------------------------------
+
+local M = {_TYPE='module', _NAME='compat_env', _VERSION='0.2.20120124'}
+
+local function check_chunk_type(s, mode)
+ local nmode = mode or 'bt'
+ local is_binary = s and #s > 0 and s:byte(1) == 27
+ if is_binary and not nmode:match'b' then
+ return nil, ("attempt to load a binary chunk (mode is '%s')"):format(mode)
+ elseif not is_binary and not nmode:match't' then
+ return nil, ("attempt to load a text chunk (mode is '%s')"):format(mode)
+ end
+ return true
+end
+
+local IS_52_LOAD = pcall(load, '')
+if IS_52_LOAD then
+ M.load = _G.load
+ M.loadfile = _G.loadfile
+else
+ -- 5.2 style `load` implemented in 5.1
+ function M.load(ld, source, mode, env)
+ local f
+ if type(ld) == 'string' then
+ local s = ld
+ local ok, err = check_chunk_type(s, mode); if not ok then return ok, err end
+ local err; f, err = loadstring(s, source); if not f then return f, err end
+ elseif type(ld) == 'function' then
+ local ld2 = ld
+ if (mode or 'bt') ~= 'bt' then
+ local first = ld()
+ local ok, err = check_chunk_type(first, mode); if not ok then return ok, err end
+ ld2 = function()
+ if first then
+ local chunk=first; first=nil; return chunk
+ else return ld() end
+ end
+ end
+ local err; f, err = load(ld2, source); if not f then return f, err end
+ else
+ error(("bad argument #1 to 'load' (function expected, got %s)"):format(type(ld)), 2)
+ end
+ if env then setfenv(f, env) end
+ return f
+ end
+
+ -- 5.2 style `loadfile` implemented in 5.1
+ function M.loadfile(filename, mode, env)
+ if (mode or 'bt') ~= 'bt' then
+ local ioerr
+ local fh, err = io.open(filename, 'rb'); if not fh then return fh, err end
+ local function ld() local chunk; chunk,ioerr = fh:read(4096); return chunk end
+ local f, err = M.load(ld, filename and '@'..filename, mode, env)
+ fh:close()
+ if not f then return f, err end
+ if ioerr then return nil, ioerr end
+ return f
+ else
+ local f, err = loadfile(filename); if not f then return f, err end
+ if env then setfenv(f, env) end
+ return f
+ end
+ end
+end
+
+if _G.setfenv then -- Lua 5.1
+ M.setfenv = _G.setfenv
+ M.getfenv = _G.getfenv
+else -- >= Lua 5.2
+ -- helper function for `getfenv`/`setfenv`
+ local function envlookup(f)
+ local name, val
+ local up = 0
+ local unknown
+ repeat
+ up=up+1; name, val = debug.getupvalue(f, up)
+ if name == '' then unknown = true end
+ until name == '_ENV' or name == nil
+ if name ~= '_ENV' then
+ up = nil
+ if unknown then error("upvalues not readable in Lua 5.2 when debug info missing", 3) end
+ end
+ return (name == '_ENV') and up, val, unknown
+ end
+
+ -- helper function for `getfenv`/`setfenv`
+ local function envhelper(f, name)
+ if type(f) == 'number' then
+ if f < 0 then
+ error(("bad argument #1 to '%s' (level must be non-negative)"):format(name), 3)
+ elseif f < 1 then
+ error("thread environments unsupported in Lua 5.2", 3) --[*]
+ end
+ f = debug.getinfo(f+2, 'f').func
+ elseif type(f) ~= 'function' then
+ error(("bad argument #1 to '%s' (number expected, got %s)"):format(type(name, f)), 2)
+ end
+ return f
+ end
+ -- [*] might simulate with table keyed by coroutine.running()
+
+ -- 5.1 style `setfenv` implemented in 5.2
+ function M.setfenv(f, t)
+ local f = envhelper(f, 'setfenv')
+ local up, val, unknown = envlookup(f)
+ if up then
+ debug.upvaluejoin(f, up, function() return up end, 1) -- unique upvalue [*]
+ debug.setupvalue(f, up, t)
+ else
+ local what = debug.getinfo(f, 'S').what
+ if what ~= 'Lua' and what ~= 'main' then -- not Lua func
+ error("'setfenv' cannot change environment of given object", 2)
+ end -- else ignore no _ENV upvalue (warning: incompatible with 5.1)
+ end
+ -- added in https://gist.github.com/2255007
+ return f
+ end
+ -- [*] http://lua-users.org/lists/lua-l/2010-06/msg00313.html
+
+ -- 5.1 style `getfenv` implemented in 5.2
+ function M.getfenv(f)
+ if f == 0 or f == nil then return _G end -- simulated behavior
+ local f = envhelper(f, 'setfenv')
+ local up, val = envlookup(f)
+ if not up then return _G end -- simulated behavior [**]
+ return val
+ end
+ -- [**] possible reasons: no _ENV upvalue, C function
+end
+
+
+return M
+
+--[[ FILE rockspec.in
+
+package = 'compat_env'
+version = '$(_VERSION)-1'
+source = {
+ url = 'https://raw.github.com/gist/1654007/$(GITID)/compat_env.lua',
+ --url = 'https://raw.github.com/gist/1654007/compat_env.lua', -- latest raw
+ --url = 'https://gist.github.com/gists/1654007/download',
+ md5 = '$(MD5)'
+}
+description = {
+ summary = 'Lua 5.1/5.2 environment compatibility functions',
+ detailed = [=[
+ Provides Lua 5.1/5.2 environment related compatibility functions.
+ This includes implementations of Lua 5.2 style `load` and `loadfile`
+ for use in Lua 5.1. It also includes Lua 5.1 style `getfenv` and `setfenv`
+ for use in Lua 5.2.
+ ]=],
+ license = 'MIT/X11',
+ homepage = 'https://gist.github.com/1654007',
+ maintainer = 'David Manura'
+}
+dependencies = {} -- Lua 5.1 or 5.2
+build = {
+ type = 'builtin',
+ modules = {
+ ['compat_env'] = 'compat_env.lua'
+ }
+}
+
+--]]---------------------------------------------------------------------
+
+--[[ FILE test.lua
+
+-- test.lua - test suite for compat_env module.
+
+local CL = require 'compat_env'
+local load = CL.load
+local loadfile = CL.loadfile
+local setfenv = CL.setfenv
+local getfenv = CL.getfenv
+
+local function checkeq(a, b, e)
+ if a ~= b then error(
+ 'not equal ['..tostring(a)..'] ['..tostring(b)..'] ['..tostring(e)..']')
+ end
+end
+local function checkerr(pat, ok, err)
+ assert(not ok, 'checkerr')
+ assert(type(err) == 'string' and err:match(pat), err)
+end
+
+-- test `load`
+checkeq(load('return 2')(), 2)
+checkerr('expected near', load'return 2 2')
+checkerr('text chunk', load('return 2', nil, 'b'))
+checkerr('text chunk', load('', nil, 'b'))
+checkerr('binary chunk', load('\027', nil, 't'))
+checkeq(load('return 2*x',nil,'bt',{x=5})(), 10)
+checkeq(debug.getinfo(load('')).source, '')
+checkeq(debug.getinfo(load('', 'foo')).source, 'foo')
+
+-- test `loadfile`
+local fh = assert(io.open('tmp.lua', 'wb'))
+fh:write('return (...) or x')
+fh:close()
+checkeq(loadfile('tmp.lua')(2), 2)
+checkeq(loadfile('tmp.lua', 't')(2), 2)
+checkerr('text chunk', loadfile('tmp.lua', 'b'))
+checkeq(loadfile('tmp.lua', nil, {x=3})(), 3)
+checkeq(debug.getinfo(loadfile('tmp.lua')).source, '@tmp.lua')
+checkeq(debug.getinfo(loadfile('tmp.lua', 't', {})).source, '@tmp.lua')
+os.remove'tmp.lua'
+
+-- test `setfenv`/`getfenv`
+x = 5
+local a,b=true; local function f(c) if a then return x,b,c end end
+setfenv(f, {x=3})
+checkeq(f(), 3)
+checkeq(getfenv(f).x, 3)
+checkerr('cannot change', pcall(setfenv, string.len, {})) -- C function
+checkeq(getfenv(string.len), _G) -- C function
+local function g()
+ setfenv(1, {x=4})
+ checkeq(getfenv(1).x, 4)
+ return x
+end
+checkeq(g(), 4) -- numeric level
+if _G._VERSION ~= 'Lua 5.1' then
+ checkerr('unsupported', pcall(setfenv, 0, {}))
+end
+checkeq(getfenv(0), _G)
+checkeq(getfenv(), _G) -- no arg
+checkeq(x, 5) -- main unaltered
+setfenv(function()end, {}) -- no upvalues, ignore
+checkeq(getfenv(function()end), _G) -- no upvaluse
+if _G._VERSION ~= 'Lua 5.1' then
+ checkeq(getfenv(setfenv(function()end, {})), _G) -- warning: incompatible with 5.1
+end
+x = nil
+
+print 'OK'
+
+--]]---------------------------------------------------------------------
+
+--[[ FILE CHANGES.txt
+0.2.20120124
+ Renamed module to compat_env (from compat_load)
+ Add getfenv/setfenv functions
+
+0.1.20120121
+ Initial public release
+--]]
diff --git a/test/lua/telescope.lua b/test/lua/telescope.lua
new file mode 100644
index 000000000..abe96bcf9
--- /dev/null
+++ b/test/lua/telescope.lua
@@ -0,0 +1,621 @@
+--[[
+The MIT License
+
+Copyright (c) 2009-2012 [Norman Clarke](mailto:norman@njclarke.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+]]--
+
+--- Telescope is a test library for Lua that allows for flexible, declarative
+-- tests. The documentation produced here is intended largely for developers
+-- working on Telescope. For information on using Telescope, please visit the
+-- project homepage at: <a href="http://github.com/norman/telescope">http://github.com/norman/telescope#readme</a>.
+-- @release 0.6
+-- @class module
+-- @module 'telescope'
+local _M = {}
+
+local compat_env = require 'compat_env'
+
+local getfenv = _G.getfenv or compat_env.getfenv
+local setfenv = _G.setfenv or compat_env.setfenv
+
+
+local _VERSION = "0.6.0"
+
+--- The status codes that can be returned by an invoked test. These should not be overidden.
+-- @name status_codes
+-- @class table
+-- @field err - This is returned when an invoked test results in an error
+-- rather than a passed or failed assertion.
+-- @field fail - This is returned when an invoked test contains one or more failing assertions.
+-- @field pass - This is returned when all of a test's assertions pass.
+-- @field pending - This is returned when a test does not have a corresponding function.
+-- @field unassertive - This is returned when an invoked test does not produce
+-- errors, but does not contain any assertions.
+local status_codes = {
+ err = 2,
+ fail = 4,
+ pass = 8,
+ pending = 16,
+ unassertive = 32
+}
+
+--- Labels used to show the various <tt>status_codes</tt> as a single character.
+-- These can be overidden if you wish.
+-- @name status_labels
+-- @class table
+-- @see status_codes
+-- @field status_codes.err 'E'
+-- @field status_codes.fail 'F'
+-- @field status_codes.pass 'P'
+-- @field status_codes.pending '?'
+-- @field status_codes.unassertive 'U'
+
+local status_labels = {
+ [status_codes.err] = 'E',
+ [status_codes.fail] = 'F',
+ [status_codes.pass] = 'P',
+ [status_codes.pending] = '?',
+ [status_codes.unassertive] = 'U'
+}
+
+--- The default names for context blocks. It defaults to "context", "spec" and
+-- "describe."
+-- @name context_aliases
+-- @class table
+local context_aliases = {"context", "describe", "spec"}
+--- The default names for test blocks. It defaults to "test," "it", "expect",
+-- "they" and "should."
+-- @name test_aliases
+-- @class table
+local test_aliases = {"test", "it", "expect", "should", "they"}
+
+--- The default names for "before" blocks. It defaults to "before" and "setup."
+-- The function in the before block will be run before each sibling test function
+-- or context.
+-- @name before_aliases
+-- @class table
+local before_aliases = {"before", "setup"}
+
+--- The default names for "after" blocks. It defaults to "after" and "teardown."
+-- The function in the after block will be run after each sibling test function
+-- or context.
+-- @name after_aliases
+-- @class table
+local after_aliases = {"after", "teardown"}
+
+-- Prefix to place before all assertion messages. Used by make_assertion().
+local assertion_message_prefix = "Assert failed: expected "
+
+--- The default assertions.
+-- These are the assertions built into telescope. You can override them or
+-- create your own custom assertions using <tt>make_assertion</tt>.
+-- <ul>
+-- <tt><li>assert_blank(a)</tt> - true if a is nil, or the empty string</li>
+-- <tt><li>assert_empty(a)</tt> - true if a is an empty table</li>
+-- <tt><li>assert_equal(a, b)</tt> - true if a == b</li>
+-- <tt><li>assert_error(f)</tt> - true if function f produces an error</li>
+-- <tt><li>assert_false(a)</tt> - true if a is false</li>
+-- <tt><li>assert_greater_than(a, b)</tt> - true if a > b</li>
+-- <tt><li>assert_gte(a, b)</tt> - true if a >= b</li>
+-- <tt><li>assert_less_than(a, b)</tt> - true if a < b</li>
+-- <tt><li>assert_lte(a, b)</tt> - true if a <= b</li>
+-- <tt><li>assert_match(a, b)</tt> - true if b is a string that matches pattern a</li>
+-- <tt><li>assert_nil(a)</tt> - true if a is nil</li>
+-- <tt><li>assert_true(a)</tt> - true if a is true</li>
+-- <tt><li>assert_type(a, b)</tt> - true if a is of type b</li>
+-- <tt><li>assert_not_blank(a)</tt> - true if a is not nil and a is not the empty string</li>
+-- <tt><li>assert_not_empty(a)</tt> - true if a is a table, and a is not empty</li>
+-- <tt><li>assert_not_equal(a, b)</tt> - true if a ~= b</li>
+-- <tt><li>assert_not_error(f)</tt> - true if function f does not produce an error</li>
+-- <tt><li>assert_not_false(a)</tt> - true if a is not false</li>
+-- <tt><li>assert_not_greater_than(a, b)</tt> - true if not (a > b)</li>
+-- <tt><li>assert_not_gte(a, b)</tt> - true if not (a >= b)</li>
+-- <tt><li>assert_not_less_than(a, b)</tt> - true if not (a < b)</li>
+-- <tt><li>assert_not_lte(a, b)</tt> - true if not (a <= b)</li>
+-- <tt><li>assert_not_match(a, b)</tt> - true if the string b does not match the pattern a</li>
+-- <tt><li>assert_not_nil(a)</tt> - true if a is not nil</li>
+-- <tt><li>assert_not_true(a)</tt> - true if a is not true</li>
+-- <tt><li>assert_not_type(a, b)</tt> - true if a is not of type b</li>
+-- </ul>
+-- @see make_assertion
+-- @name assertions
+-- @class table
+local assertions = {}
+
+--- Create a custom assertion.
+-- This creates an assertion along with a corresponding negative assertion. It
+-- is used internally by telescope to create the default assertions.
+-- @param name The base name of the assertion.
+-- <p>
+-- The name will be used as the basis of the positive and negative assertions;
+-- i.e., the name <tt>equal</tt> would be used to create the assertions
+-- <tt>assert_equal</tt> and <tt>assert_not_equal</tt>.
+-- </p>
+-- @param message The base message that will be shown.
+-- <p>
+-- The assertion message is what is shown when the assertion fails. It will be
+-- prefixed with the string in <tt>telescope.assertion_message_prefix</tt>.
+-- The variables passed to <tt>telescope.make_assertion</tt> are interpolated
+-- in the message string using <tt>string.format</tt>. When creating the
+-- inverse assertion, the message is reused, with <tt>" to be "</tt> replaced
+-- by <tt>" not to be "</tt>. Hence a recommended format is something like:
+-- <tt>"%s to be similar to %s"</tt>.
+-- </p>
+-- @param func The assertion function itself.
+-- <p>
+-- The assertion function can have any number of arguments.
+-- </p>
+-- @usage <tt>make_assertion("equal", "%s to be equal to %s", function(a, b)
+-- return a == b end)</tt>
+-- @function make_assertion
+local function make_assertion(name, message, func)
+ local num_vars = 0
+ -- if the last vararg ends up nil, we'll need to pad the table with nils so
+ -- that string.format gets the number of args it expects
+ local format_message
+ if type(message) == "function" then
+ format_message = message
+ else
+ for _, _ in message:gmatch("%%s") do num_vars = num_vars + 1 end
+ format_message = function(message, ...)
+ local a = {}
+ local args = {...}
+ local nargs = select('#', ...)
+ if nargs > num_vars then
+ local userErrorMessage = args[num_vars+1]
+ if type(userErrorMessage) == "string" then
+ return(assertion_message_prefix .. userErrorMessage)
+ else
+ error(string.format('assert_%s expected %d arguments but got %d', name, num_vars, #args))
+ end
+ end
+ for i = 1, nargs do a[i] = tostring(v) end
+ for i = nargs+1, num_vars do a[i] = 'nil' end
+ return (assertion_message_prefix .. message):format(unpack(a))
+ end
+ end
+
+ assertions["assert_" .. name] = function(...)
+ if assertion_callback then assertion_callback(...) end
+ if not func(...) then
+ error({format_message(message, ...), debug.traceback()})
+ end
+ end
+end
+
+--- (local) Return a table with table t's values as keys and keys as values.
+-- @param t The table.
+local function invert_table(t)
+ local t2 = {}
+ for k, v in pairs(t) do t2[v] = k end
+ return t2
+end
+
+-- (local) Truncate a string "s" to length "len", optionally followed by the
+-- string given in "after" if truncated; for example, truncate_string("hello
+-- world", 3, "...")
+-- @param s The string to truncate.
+-- @param len The desired length.
+-- @param after A string to append to s, if it is truncated.
+local function truncate_string(s, len, after)
+ if #s <= len then
+ return s
+ else
+ local s = s:sub(1, len):gsub("%s*$", '')
+ if after then return s .. after else return s end
+ end
+end
+
+--- (local) Filter a table's values by function. This function iterates over a
+-- table , returning only the table entries that, when passed into function f,
+-- yield a truthy value.
+-- @param t The table over which to iterate.
+-- @param f The filter function.
+local function filter(t, f)
+ local a, b
+ return function()
+ repeat a, b = next(t, a)
+ if not b then return end
+ if f(a, b) then return a, b end
+ until not b
+ end
+end
+
+--- (local) Finds the value in the contexts table indexed with i, and returns a table
+-- of i's ancestor contexts.
+-- @param i The index in the <tt>contexts</tt> table to get ancestors for.
+-- @param contexts The table in which to find the ancestors.
+local function ancestors(i, contexts)
+ if i == 0 then return end
+ local a = {}
+ local function func(j)
+ if contexts[j].parent == 0 then return nil end
+ table.insert(a, contexts[j].parent)
+ func(contexts[j].parent)
+ end
+ func(i)
+ return a
+end
+
+make_assertion("blank", "'%s' to be blank", function(a) return a == '' or a == nil end)
+make_assertion("empty", "'%s' to be an empty table", function(a) return not next(a) end)
+make_assertion("equal", "'%s' to be equal to '%s'", function(a, b) return a == b end)
+make_assertion("error", "result to be an error", function(f) return not pcall(f) end)
+make_assertion("false", "'%s' to be false", function(a) return a == false end)
+make_assertion("greater_than", "'%s' to be greater than '%s'", function(a, b) return a > b end)
+make_assertion("gte", "'%s' to be greater than or equal to '%s'", function(a, b) return a >= b end)
+make_assertion("less_than", "'%s' to be less than '%s'", function(a, b) return a < b end)
+make_assertion("lte", "'%s' to be less than or equal to '%s'", function(a, b) return a <= b end)
+make_assertion("match", "'%s' to be a match for %s", function(a, b) return (tostring(b)):match(a) end)
+make_assertion("nil", "'%s' to be nil", function(a) return a == nil end)
+make_assertion("true", "'%s' to be true", function(a) return a == true end)
+make_assertion("type", "'%s' to be a %s", function(a, b) return type(a) == b end)
+
+make_assertion("not_blank", "'%s' not to be blank", function(a) return a ~= '' and a ~= nil end)
+make_assertion("not_empty", "'%s' not to be an empty table", function(a) return not not next(a) end)
+make_assertion("not_equal", "'%s' not to be equal to '%s'", function(a, b) return a ~= b end)
+make_assertion("not_error", "result not to be an error", function(f) return not not pcall(f) end)
+make_assertion("not_match", "'%s' not to be a match for %s", function(a, b) return not (tostring(b)):match(a) end)
+make_assertion("not_nil", "'%s' not to be nil", function(a) return a ~= nil end)
+make_assertion("not_type", "'%s' not to be a %s", function(a, b) return type(a) ~= b end)
+
+--- Build a contexts table from the test file or function given in <tt>target</tt>.
+-- If the optional <tt>contexts</tt> table argument is provided, then the
+-- resulting contexts will be added to it.
+-- <p>
+-- The resulting contexts table's structure is as follows:
+-- </p>
+-- <code>
+-- {
+-- {parent = 0, name = "this is a context", context = true},
+-- {parent = 1, name = "this is a nested context", context = true},
+-- {parent = 2, name = "this is a test", test = function},
+-- {parent = 2, name = "this is another test", test = function},
+-- {parent = 0, name = "this is test outside any context", test = function},
+-- }
+-- </code>
+-- @param contexts A optional table in which to collect the resulting contexts
+-- and function.
+-- @function load_contexts
+local function load_contexts(target, contexts)
+ local env = {}
+ local current_index = 0
+ local context_table = contexts or {}
+
+ local function context_block(name, func)
+ table.insert(context_table, {parent = current_index, name = name, context = true})
+ local previous_index = current_index
+ current_index = #context_table
+ func()
+ current_index = previous_index
+ end
+
+ local function test_block(name, func)
+ local test_table = {name = name, parent = current_index, test = func or true}
+ if current_index ~= 0 then
+ test_table.context_name = context_table[current_index].name
+ else
+ test_table.context_name = 'top level'
+ end
+ table.insert(context_table, test_table)
+ end
+
+ local function before_block(func)
+ context_table[current_index].before = func
+ end
+
+ local function after_block(func)
+ context_table[current_index].after = func
+ end
+
+ for _, v in ipairs(after_aliases) do env[v] = after_block end
+ for _, v in ipairs(before_aliases) do env[v] = before_block end
+ for _, v in ipairs(context_aliases) do env[v] = context_block end
+ for _, v in ipairs(test_aliases) do env[v] = test_block end
+
+ -- Set these functions in the module's meta table to allow accessing
+ -- telescope's test and context functions without env tricks. This will
+ -- however add tests to a context table used inside the module, so multiple
+ -- test files will add tests to the same top-level context, which may or may
+ -- not be desired.
+ setmetatable(_M, {__index = env})
+
+ setmetatable(env, {__index = _G})
+
+ local func, err = type(target) == 'string' and assert(loadfile(target)) or target
+ if err then error(err) end
+ setfenv(func, env)()
+ return context_table
+end
+
+-- in-place table reverse.
+function table.reverse(t)
+ local len = #t+1
+ for i=1, (len-1)/2 do
+ t[i], t[len-i] = t[len-i], t[i]
+ end
+end
+
+--- Run all tests.
+-- This function will exectute each function in the contexts table.
+-- @param contexts The contexts created by <tt>load_contexts</tt>.
+-- @param callbacks A table of callback functions to be invoked before or after
+-- various test states.
+-- <p>
+-- There is a callback for each test <tt>status_code</tt>, and callbacks to run
+-- before or after each test invocation regardless of outcome.
+-- </p>
+-- <ul>
+-- <li>after - will be invoked after each test</li>
+-- <li>before - will be invoked before each test</li>
+-- <li>err - will be invoked after each test which results in an error</li>
+-- <li>fail - will be invoked after each failing test</li>
+-- <li>pass - will be invoked after each passing test</li>
+-- <li>pending - will be invoked after each pending test</li>
+-- <li>unassertive - will be invoked after each test which doesn't assert
+-- anything</li>
+-- </ul>
+-- <p>
+-- Callbacks can be used, for example, to drop into a debugger upon a failed
+-- assertion or error, for profiling, or updating a GUI progress meter.
+-- </p>
+-- @param test_filter A function to filter tests that match only conditions that you specify.
+-- <p>
+-- For example, the folling would allow you to run only tests whose name matches a pattern:
+-- </p>
+-- <p>
+-- <code>
+-- function(t) return t.name:match("%s* lexer") end
+-- </code>
+-- </p>
+-- @return A table of result tables. Each result table has the following
+-- fields:
+-- <ul>
+-- <li>assertions_invoked - the number of assertions the test invoked</li>
+-- <li>context - the name of the context</li>
+-- <li>message - a table with an error message and stack trace</li>
+-- <li>name - the name of the test</li>
+-- <li>status_code - the resulting status code</li>
+-- <li>status_label - the label for the status_code</li>
+-- </ul>
+-- @see load_contexts
+-- @see status_codes
+-- @function run
+local function run(contexts, callbacks, test_filter)
+
+ local results = {}
+ local status_names = invert_table(status_codes)
+ local test_filter = test_filter or function(a) return a end
+
+ -- Setup a new environment suitable for running a new test
+ local function newEnv()
+ local env = {}
+
+ -- Make sure globals are accessible in the new environment
+ setmetatable(env, {__index = _G})
+
+ -- Setup all the assert functions in the new environment
+ for k, v in pairs(assertions) do
+ setfenv(v, env)
+ env[k] = v
+ end
+
+ return env
+ end
+
+ local env = newEnv()
+
+ local function invoke_callback(name, test)
+ if not callbacks then return end
+ if type(callbacks[name]) == "table" then
+ for _, c in ipairs(callbacks[name]) do c(test) end
+ elseif callbacks[name] then
+ callbacks[name](test)
+ end
+ end
+
+ local function invoke_test(func)
+ local assertions_invoked = 0
+ env.assertion_callback = function()
+ assertions_invoked = assertions_invoked + 1
+ end
+ setfenv(func, env)
+ local result, message = xpcall(func, debug.traceback)
+ if result and assertions_invoked > 0 then
+ return status_codes.pass, assertions_invoked, nil
+ elseif result then
+ return status_codes.unassertive, 0, nil
+ elseif type(message) == "table" then
+ return status_codes.fail, assertions_invoked, message
+ else
+ return status_codes.err, assertions_invoked, {message, debug.traceback()}
+ end
+ end
+
+ for i, v in filter(contexts, function(i, v) return v.test and test_filter(v) end) do
+ env = newEnv() -- Setup a new environment for this test
+
+ local ancestors = ancestors(i, contexts)
+ local context_name = 'Top level'
+ if contexts[i].parent ~= 0 then
+ context_name = contexts[contexts[i].parent].name
+ end
+ local result = {
+ assertions_invoked = 0,
+ name = contexts[i].name,
+ context = context_name,
+ test = i
+ }
+ table.sort(ancestors)
+ -- this "before" is the test callback passed into the runner
+ invoke_callback("before", result)
+
+ -- run all the "before" blocks/functions
+ for _, a in ipairs(ancestors) do
+ if contexts[a].before then
+ setfenv(contexts[a].before, env)
+ contexts[a].before()
+ end
+ end
+
+ -- check if it's a function because pending tests will just have "true"
+ if type(v.test) == "function" then
+ result.status_code, result.assertions_invoked, result.message = invoke_test(v.test)
+ invoke_callback(status_names[result.status_code], result)
+ else
+ result.status_code = status_codes.pending
+ invoke_callback("pending", result)
+ end
+ result.status_label = status_labels[result.status_code]
+
+ -- Run all the "after" blocks/functions
+ table.reverse(ancestors)
+ for _, a in ipairs(ancestors) do
+ if contexts[a].after then
+ setfenv(contexts[a].after, env)
+ contexts[a].after()
+ end
+ end
+
+ invoke_callback("after", result)
+ results[i] = result
+ end
+
+ return results
+
+end
+
+--- Return a detailed report for each context, with the status of each test.
+-- @param contexts The contexts returned by <tt>load_contexts</tt>.
+-- @param results The results returned by <tt>run</tt>.
+-- @function test_report
+local function test_report(contexts, results)
+
+ local buffer = {}
+ local leading_space = " "
+ local level = 0
+ local line_char = "-"
+ local previous_level = 0
+ local status_format_len = 3
+ local status_format = "[%s]"
+ local width = 72
+ local context_name_format = "%-" .. width - status_format_len .. "s"
+ local function_name_format = "%-" .. width - status_format_len .. "s"
+
+ local function space()
+ return leading_space:rep(level - 1)
+ end
+
+ local function add_divider()
+ table.insert(buffer, line_char:rep(width))
+ end
+ add_divider()
+ for i, item in ipairs(contexts) do
+ local ancestors = ancestors(i, contexts)
+ previous_level = level or 0
+ level = #ancestors
+ -- the 4 here is the length of "..." plus one space of padding
+ local name = truncate_string(item.name, width - status_format_len - 4 - #ancestors, '...')
+ if previous_level ~= level and level == 0 then add_divider() end
+ if item.context then
+ table.insert(buffer, context_name_format:format(space() .. name .. ':'))
+ elseif results[i] then
+ table.insert(buffer, function_name_format:format(space() .. name) ..
+ status_format:format(results[i].status_label))
+ end
+ end
+ add_divider()
+ return table.concat(buffer, "\n")
+
+end
+
+--- Return a table of stack traces for tests which produced a failure or an error.
+-- @param contexts The contexts returned by <tt>load_contexts</tt>.
+-- @param results The results returned by <tt>run</tt>.
+-- @function error_report
+local function error_report(contexts, results)
+ local buffer = {}
+ for _, r in filter(results, function(i, r) return r.message end) do
+ local name = contexts[r.test].name
+ table.insert(buffer, name .. ":\n" .. r.message[1] .. "\n" .. r.message[2])
+ end
+ if #buffer > 0 then return table.concat(buffer, "\n") end
+end
+
+--- Get a one-line report and a summary table with the status counts. The
+-- counts given are: total tests, assertions, passed tests, failed tests,
+-- pending tests, and tests which didn't assert anything.
+-- @return A report that can be printed
+-- @return A table with the various counts. Its fields are:
+-- <tt>assertions</tt>, <tt>errors</tt>, <tt>failed</tt>, <tt>passed</tt>,
+-- <tt>pending</tt>, <tt>tests</tt>, <tt>unassertive</tt>.
+-- @param contexts The contexts returned by <tt>load_contexts</tt>.
+-- @param results The results returned by <tt>run</tt>.
+-- @function summary_report
+local function summary_report(contexts, results)
+ local r = {
+ assertions = 0,
+ errors = 0,
+ failed = 0,
+ passed = 0,
+ pending = 0,
+ tests = 0,
+ unassertive = 0
+ }
+ for _, v in pairs(results) do
+ r.tests = r.tests + 1
+ r.assertions = r.assertions + v.assertions_invoked
+ if v.status_code == status_codes.err then r.errors = r.errors + 1
+ elseif v.status_code == status_codes.fail then r.failed = r.failed + 1
+ elseif v.status_code == status_codes.pass then r.passed = r.passed + 1
+ elseif v.status_code == status_codes.pending then r.pending = r.pending + 1
+ elseif v.status_code == status_codes.unassertive then r.unassertive = r.unassertive + 1
+ end
+ end
+ local buffer = {}
+ for _, k in ipairs({"tests", "passed", "assertions", "failed", "errors", "unassertive", "pending"}) do
+ local number = r[k]
+ local label = k
+ if number == 1 then
+ label = label:gsub("s$", "")
+ end
+ table.insert(buffer, ("%d %s"):format(number, label))
+ end
+ return table.concat(buffer, " "), r
+end
+
+_M.after_aliases = after_aliases
+_M.make_assertion = make_assertion
+_M.assertion_message_prefix = assertion_message_prefix
+_M.before_aliases = before_aliases
+_M.context_aliases = context_aliases
+_M.error_report = error_report
+_M.load_contexts = load_contexts
+_M.run = run
+_M.test_report = test_report
+_M.status_codes = status_codes
+_M.status_labels = status_labels
+_M.summary_report = summary_report
+_M.test_aliases = test_aliases
+_M.version = _VERSION
+_M._VERSION = _VERSION
+
+return _M
diff --git a/test/lua/tests.lua b/test/lua/tests.lua
new file mode 100644
index 000000000..0f6b06b4b
--- /dev/null
+++ b/test/lua/tests.lua
@@ -0,0 +1,22 @@
+-- Run all unit tests in 'unit' directory
+
+local telescope = require "telescope"
+
+local contexts = {}
+
+for _,t in ipairs(tests_list) do
+ telescope.load_contexts(t, contexts)
+end
+local buffer = {}
+local results = telescope.run(contexts, callbacks, test_pattern)
+local summary, data = telescope.summary_report(contexts, results)
+table.insert(buffer, telescope.test_report(contexts, results))
+
+if #buffer > 0 then print(table.concat(buffer, "\n")) end
+
+for _, v in pairs(results) do
+ if v.status_code == telescope.status_codes.err or
+ v.status_code == telescope.status_codes.fail then
+ os.exit(1)
+ end
+end \ No newline at end of file
diff --git a/test/lua/rsa.lua b/test/lua/unit/rsa.lua
index 75d07fd30..75d07fd30 100644
--- a/test/lua/rsa.lua
+++ b/test/lua/unit/rsa.lua
diff --git a/test/lua/test.data b/test/lua/unit/test.data
index 05ec159ee..05ec159ee 100644
--- a/test/lua/test.data
+++ b/test/lua/unit/test.data
diff --git a/test/lua/testkey b/test/lua/unit/testkey
index 4a0325b42..4a0325b42 100644
--- a/test/lua/testkey
+++ b/test/lua/unit/testkey
diff --git a/test/lua/testkey.pub b/test/lua/unit/testkey.pub
index 6407aa046..6407aa046 100644
--- a/test/lua/testkey.pub
+++ b/test/lua/unit/testkey.pub
diff --git a/test/rspamd_lua_test.c b/test/rspamd_lua_test.c
index cd3b152cd..bdc23e91c 100644
--- a/test/rspamd_lua_test.c
+++ b/test/rspamd_lua_test.c
@@ -26,54 +26,95 @@
#include "util.h"
#include "lua/lua_common.h"
-static const char *lua_src = "./lua";
+static const char *lua_src = BUILDROOT "/test/lua/tests.lua";
+
+static int
+traceback (lua_State *L)
+{
+ if (!lua_isstring (L, 1)) {
+ return 1;
+ }
+
+ lua_getfield (L, LUA_GLOBALSINDEX, "debug");
+
+ if (!lua_istable(L, -1)) {
+ lua_pop(L, 1);
+ return 1;
+ }
+
+ lua_getfield (L, -1, "traceback");
+
+ if (!lua_isfunction(L, -1)) {
+ lua_pop(L, 2);
+ return 1;
+ }
+ lua_pushvalue (L, 1);
+ lua_pushinteger (L, 2);
+ lua_call(L, 2, 1);
+
+ return 1;
+}
void
-rspamd_lua_test_func (int argc, char **argv)
+rspamd_lua_test_func (void)
{
lua_State *L = rspamd_lua_init (NULL);
- gchar rp[PATH_MAX], path_buf[PATH_MAX];
+ gchar *rp, rp_buf[PATH_MAX], path_buf[PATH_MAX], *tmp, *dir, *pattern;
const gchar *old_path;
- guint i;
+ glob_t globbuf;
+ gint i, len;
- msg_info ("Starting lua tests");
+ rspamd_printf ("Starting lua tests\n");
- if (realpath (lua_src, rp) == NULL) {
- msg_err ("cannod find path %s: %s", lua_src, strerror (errno));
+ if ((rp = realpath (lua_src, rp_buf)) == NULL) {
+ msg_err ("cannot find path %s: %s",
+ lua_src, strerror (errno));
g_assert (0);
}
+ tmp = g_strdup (rp);
+ dir = dirname (tmp);
/* Set lua path */
lua_getglobal (L, "package");
lua_getfield (L, -1, "path");
old_path = luaL_checkstring (L, -1);
- rspamd_snprintf (path_buf, sizeof (path_buf), "%s;%s/?.lua;%s/busted/?.lua",
- old_path, rp, rp);
+ rspamd_snprintf (path_buf, sizeof (path_buf), "%s;%s/?.lua;%s/unit/?.lua",
+ old_path, dir, dir);
lua_pop (L, 1);
lua_pushstring (L, path_buf);
lua_setfield (L, -2, "path");
lua_pop (L, 1);
- lua_getglobal (L, "arg");
+ lua_newtable (L);
- if (lua_type (L, -1) != LUA_TTABLE) {
- lua_newtable (L);
- }
+ globbuf.gl_offs = 0;
+ len = strlen (dir) + sizeof ("/unit/") + sizeof ("*.lua");
+ pattern = g_malloc (len);
+ rspamd_snprintf (pattern, len, "%s/unit/%s", dir, "*.lua");
- for (i = 0; i < argc - 1; i ++) {
- lua_pushinteger (L, i + 1);
- lua_pushstring (L, argv[i]);
- lua_settable (L, -3);
+ if (glob (pattern, GLOB_DOOFFS, NULL, &globbuf) == 0) {
+ for (i = 0; i < (gint)globbuf.gl_pathc; i++) {
+ lua_pushinteger (L, i + 1);
+ lua_pushstring (L, globbuf.gl_pathv[i]);
+ lua_settable (L, -3);
+ }
+ globfree (&globbuf);
+ g_free (pattern);
+ }
+ else {
+ msg_err ("pattern %s doesn't match: %s", pattern,
+ strerror (errno));
+ g_assert (0);
}
- lua_setglobal (L, "arg");
- lua_pop (L, 1);
+ lua_setglobal (L, "tests_list");
+
+ lua_pushcfunction (L, traceback);
+ luaL_loadfile (L, rp);
- rspamd_snprintf (path_buf, sizeof (path_buf),
- "require 'busted.runner'({ batch = true })");
- if (luaL_dostring (L, path_buf) != 0) {
- rspamd_fprintf (stderr, "run test failed: %s", lua_tostring (L, -1));
+ if (lua_pcall (L, 0, 0, lua_gettop (L) - 1) != 0) {
+ msg_err ("run test failed: %s", lua_tostring (L, -1));
g_assert (0);
}
diff --git a/test/rspamd_test_suite.c b/test/rspamd_test_suite.c
index a70a0102f..1023c5cf0 100644
--- a/test/rspamd_test_suite.c
+++ b/test/rspamd_test_suite.c
@@ -13,39 +13,35 @@ main (int argc, char **argv)
{
struct rspamd_config *cfg;
- if (argc > 0 && strcmp (argv[1], "lua") == 0) {
- /* Special lua testing mode */
- rspamd_lua_test_func (argc - 1, &argv[2]);
- }
+ rspamd_main = (struct rspamd_main *)g_malloc (sizeof (struct rspamd_main));
+ memset (rspamd_main, 0, sizeof (struct rspamd_main));
+ rspamd_main->server_pool = rspamd_mempool_new (rspamd_mempool_suggest_size ());
+ rspamd_main->cfg = (struct rspamd_config *)g_malloc (sizeof (struct rspamd_config));
+ cfg = rspamd_main->cfg;
+ bzero (cfg, sizeof (struct rspamd_config));
+ cfg->cfg_pool = rspamd_mempool_new (rspamd_mempool_suggest_size ());
+ cfg->log_type = RSPAMD_LOG_CONSOLE;
+ cfg->log_level = G_LOG_LEVEL_INFO;
- g_test_init (&argc, &argv, NULL);
+ rspamd_set_logger (cfg, g_quark_from_static_string("rspamd-test"), rspamd_main);
+ (void)rspamd_log_open (rspamd_main->logger);
- rspamd_main = (struct rspamd_main *)g_malloc (sizeof (struct rspamd_main));
+ g_test_init (&argc, &argv, NULL);
#if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION <= 30))
g_thread_init (NULL);
#endif
g_mime_init (0);
- memset (rspamd_main, 0, sizeof (struct rspamd_main));
- rspamd_main->server_pool = rspamd_mempool_new (rspamd_mempool_suggest_size ());
- rspamd_main->cfg = (struct rspamd_config *)g_malloc (sizeof (struct rspamd_config));
- cfg = rspamd_main->cfg;
- bzero (cfg, sizeof (struct rspamd_config));
- cfg->cfg_pool = rspamd_mempool_new (rspamd_mempool_suggest_size ());
base = event_init ();
if (g_test_verbose ()) {
cfg->log_level = G_LOG_LEVEL_DEBUG;
+ rspamd_set_logger (cfg, g_quark_from_static_string("rspamd-test"), rspamd_main);
+ (void)rspamd_log_reopen (rspamd_main->logger);
}
- else {
- cfg->log_level = G_LOG_LEVEL_INFO;
- }
- cfg->log_type = RSPAMD_LOG_CONSOLE;
- /* First set logger to console logger */
- rspamd_set_logger (cfg, g_quark_from_static_string("rspamd-test"), rspamd_main);
- (void)rspamd_log_open (rspamd_main->logger);
+
g_log_set_default_handler (rspamd_glib_log_function, rspamd_main->logger);
g_test_add_func ("/rspamd/mem_pool", rspamd_mem_pool_test_func);
@@ -61,6 +57,7 @@ main (int argc, char **argv)
g_test_add_func ("/rspamd/upstream", rspamd_upstream_test_func);
g_test_add_func ("/rspamd/shingles", rspamd_shingles_test_func);
g_test_add_func ("/rspamd/http", rspamd_http_test_func);
+ g_test_add_func ("/rspamd/lua", rspamd_lua_test_func);
g_test_run ();
diff --git a/test/tests.h b/test/tests.h
index ae69d9d96..1d14bde4b 100644
--- a/test/tests.h
+++ b/test/tests.h
@@ -41,6 +41,6 @@ void rspamd_shingles_test_func (void);
void rspamd_http_test_func (void);
-void rspamd_lua_test_func (int argc, char **argv);
+void rspamd_lua_test_func (void);
#endif