]> source.dussan.org Git - rspamd.git/commitdiff
Rework lua tests one more time.
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Sat, 28 Feb 2015 00:11:56 +0000 (00:11 +0000)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Sat, 28 Feb 2015 00:11:56 +0000 (00:11 +0000)
16 files changed:
config.h.in
test/CMakeLists.txt
test/lua/compat_env.lua [new file with mode: 0644]
test/lua/rsa.lua [deleted file]
test/lua/telescope.lua [new file with mode: 0644]
test/lua/test.data [deleted file]
test/lua/testkey [deleted file]
test/lua/testkey.pub [deleted file]
test/lua/tests.lua [new file with mode: 0644]
test/lua/unit/rsa.lua [new file with mode: 0644]
test/lua/unit/test.data [new file with mode: 0644]
test/lua/unit/testkey [new file with mode: 0644]
test/lua/unit/testkey.pub [new file with mode: 0644]
test/rspamd_lua_test.c
test/rspamd_test_suite.c
test/tests.h

index e45f95f4cf96c0ab55435a0a503b463c63ae0906..37267d468e8c75a65733c786e9f1dc4b29e63341 100644 (file)
 
 #define MODULES_NUM        ${RSPAMD_MODULES_NUM}
 
+#define BUILDROOT "${CMAKE_BINARY_DIR}"
+
 /* sys/types */
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
index b4fa018d2772ad9259290de97219b78e7ae213f6..18a826026f1641c2aa915987967fd3c997020c69 100644 (file)
@@ -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 (file)
index 0000000..2ecd4b6
--- /dev/null
@@ -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/rsa.lua b/test/lua/rsa.lua
deleted file mode 100644 (file)
index 75d07fd..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
--- Test rsa signing
-
-describe("rsa signarture test", function()
-  local rsa_privkey = require "rspamd_rsa_privkey"
-  local rsa_pubkey = require "rspamd_rsa_pubkey"
-  local rsa_signature = require "rspamd_rsa_signature"
-  local rsa = require "rspamd_rsa"
-  local pubkey = 'testkey.pub'
-  local privkey = 'testkey'
-  local data = 'test.data'
-  local signature = 'test.sig'
-
-  -- Signing test
-  local rsa_key = rsa_privkey.load(string.format('%s/%s', test_dir, privkey))
-
-  if not rsa_key then
-    return -1
-  end
-
-  local rsa_sig = rsa.sign_file(rsa_key, string.format('%s/%s', test_dir, data))
-
-  if not rsa_sig then
-    return -1
-  end
-
-  rsa_sig:save(string.format('%s/%s', test_dir, signature), true)
-
-  -- Verifying test
-  rsa_key = rsa_pubkey.load(string.format('%s/%s', test_dir, pubkey))
-
-  if not rsa_key then
-    return -1
-  end
-
-  rsa_sig = rsa_signature.load(string.format('%s/%s', test_dir, signature))
-
-  if not rsa_sig then
-    return -1
-  end
-
-  if not rsa.verify_file(rsa_key, rsa_sig, string.format('%s/%s', test_dir, data)) then
-    return -1
-  end
-
-end)
diff --git a/test/lua/telescope.lua b/test/lua/telescope.lua
new file mode 100644 (file)
index 0000000..abe96bc
--- /dev/null
@@ -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/test.data b/test/lua/test.data
deleted file mode 100644 (file)
index 05ec159..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas et adipiscing nunc, non auctor justo. Nunc consectetur nunc eget nibh pulvinar, quis consequat tortor mollis. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In eu lectus in mi sodales fermentum. Pellentesque id sapien in augue sodales porta laoreet vel odio. Nam semper massa turpis, sit amet tempus nulla scelerisque non. Sed porttitor sapien est, vitae imperdiet nulla blandit vel.
-
-Proin venenatis neque urna, sit amet elementum risus accumsan in. Nam lectus augue, porttitor a accumsan porta, dignissim ut magna. Cras eleifend orci at eros porttitor posuere. Vestibulum semper accumsan purus, vel varius nisl porta sed. Duis id consectetur ligula, id vestibulum erat. Ut ultricies viverra ipsum sagittis semper. Proin cursus ante at justo pharetra placerat. Donec ultrices eros orci, sed congue libero mattis nec. Ut fringilla quis mauris non condimentum. Proin ante risus, imperdiet et elit vitae, aliquet egestas tortor.
-
-Curabitur tempus, lacus in vehicula pulvinar, odio nisl porta sem, sit amet aliquet metus tellus rhoncus nulla. Morbi condimentum enim vitae lorem imperdiet, ac feugiat nisl suscipit. Proin eleifend laoreet tortor. Sed id risus elementum, sollicitudin nibh et, luctus augue. Cras consequat nec diam in volutpat. Nam tempor nisi nec risus tempus varius quis eget mi. Nunc mauris dolor, imperdiet eget nisi ut, aliquet vulputate massa. Sed consequat diam vitae malesuada vulputate.
-
-Duis ac mollis ante, et suscipit eros. Aenean vel risus nec nisi lacinia aliquam. Phasellus auctor hendrerit facilisis. Etiam at dolor vestibulum, varius quam tempus, adipiscing dolor. Vestibulum erat dui, semper eget orci eget, iaculis laoreet ipsum. Nullam id quam at odio pharetra vestibulum. Morbi porttitor sagittis nunc sed pellentesque. Quisque semper orci ac metus molestie egestas.
-
-Donec mattis commodo magna sit amet pellentesque. In et varius urna. Suspendisse eget tellus nulla. Suspendisse potenti. Praesent a ante sit amet tellus pellentesque ornare id sit amet eros. Donec sed dignissim ligula, nec condimentum nulla. Etiam fermentum leo sit amet tincidunt fringilla. In semper lacus sit amet laoreet volutpat. Curabitur ultricies sem in dui eleifend, at aliquam sapien adipiscing. Aliquam erat volutpat. Maecenas felis eros, posuere quis placerat a, imperdiet ac lectus. Phasellus volutpat lorem sed nulla vehicula pulvinar. Pellentesque ipsum ante, posuere id eros non, vehicula commodo augue. Proin vitae tempor lectus, at commodo elit. Donec luctus commodo erat. Curabitur faucibus, arcu tincidunt vehicula rutrum, quam turpis hendrerit sapien, nec vehicula est est ut turpis.
-
-Suspendisse elementum ligula nibh. Nullam interdum tempor metus sed ultrices. Proin nec venenatis tellus. Donec non tortor at ipsum ultrices sagittis. Integer lectus augue, lacinia quis ante iaculis, iaculis volutpat odio. Fusce porttitor quis nisi vitae condimentum. Nam luctus placerat fringilla. Praesent vulputate aliquet convallis. Nunc vel sagittis sem. Vestibulum vitae ante a ipsum pulvinar faucibus pulvinar ac nulla. Morbi mattis nulla et dui egestas, vitae volutpat nisi pretium. Ut ullamcorper ac orci nec lacinia. Proin ac condimentum diam. Nulla blandit, tellus sit amet volutpat suscipit, purus eros iaculis odio, in euismod nulla nisl at urna. Aliquam erat volutpat. Pellentesque hendrerit dui eget lobortis congue.
-
-Etiam condimentum adipiscing sollicitudin. Curabitur at rhoncus sem. Donec dignissim, erat sodales malesuada fringilla, metus est vehicula nulla, nec volutpat lacus neque ut libero. Suspendisse potenti. Nam vel facilisis massa. Morbi eget quam urna. Suspendisse quis lacus eget justo placerat pulvinar sit amet a nunc. Cras porta magna nec risus feugiat, pharetra rhoncus mauris convallis. Integer pretium dolor massa, a consectetur velit laoreet sed. Nunc rutrum convallis orci, sit amet congue sapien iaculis at.
-
-Etiam leo nibh, ultricies eget dictum non, euismod nec quam. Cras luctus mauris odio, in dapibus risus rhoncus et. Integer ullamcorper elit neque. Nam vel nisl sit amet nunc gravida laoreet. Suspendisse sed neque id nulla tempus laoreet. Aliquam interdum lorem quis erat lacinia, in congue velit euismod. Morbi sit amet viverra dolor, lacinia pretium massa. Proin eu rutrum augue, sit amet condimentum enim. Pellentesque sodales, justo quis gravida lacinia, nulla dolor accumsan sapien, non hendrerit urna dui quis nibh. In posuere non elit eu hendrerit.
-
-Nulla eget justo ultrices tellus tristique cursus. Maecenas in sem et tellus pulvinar rhoncus quis ac urna. Vestibulum imperdiet nisl nibh, at suscipit mi varius eu. Vestibulum adipiscing tellus sed ipsum vehicula, vitae convallis felis ornare. Sed commodo varius adipiscing. Pellentesque eget magna diam. Etiam ac ante ligula. Proin mattis quis odio id placerat. Nam luctus, sapien rhoncus iaculis elementum, diam magna condimentum velit, eu pellentesque orci tortor eget dui. Fusce adipiscing feugiat nunc, ac iaculis lorem ultrices sit amet. Proin vel felis eget nulla consectetur congue vel sed sapien. Morbi sed aliquam neque. Interdum et malesuada fames ac ante ipsum primis in faucibus. In volutpat velit ut est sodales, eget porttitor massa faucibus.
-
-Nunc vel fermentum augue, vitae convallis metus. Pellentesque accumsan ipsum nec condimentum pharetra. Quisque id nisi egestas, ultrices mi nec, faucibus orci. Aenean aliquam, ipsum lobortis tristique facilisis, neque purus commodo est, semper semper nunc metus quis ligula. Praesent faucibus est vel quam eleifend, a accumsan diam tempus. Integer pellentesque rhoncus vehicula. Donec sit amet mollis sem. Vivamus et ipsum at justo iaculis imperdiet a vel felis. Morbi felis augue, condimentum sit amet ligula et, mollis adipiscing purus. In vulputate nibh nec mattis fermentum. In ornare felis in aliquam cursus. Suspendisse magna mi, vehicula ut egestas eu, egestas non eros. Nunc non sem massa. Pellentesque mattis, lacus sit amet luctus scelerisque, lacus est ultricies erat, ut tincidunt nisl orci id leo. Maecenas ut massa volutpat, aliquet odio placerat, luctus massa. Ut sed odio tincidunt, luctus arcu in, dictum leo. 
diff --git a/test/lua/testkey b/test/lua/testkey
deleted file mode 100644 (file)
index 4a0325b..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEowIBAAKCAQEAxUYMGsqMbNZl4vw65AfivuC5rXDzbvP8zqj96L8t9M/2bV7D
-f1k4Pit+TKBczhs3HolQStq46AmrhoyNbLJxyaA8g+8ETXOhHzQUR74ud/xQaPqx
-02E02kbR3LnQTp/wdrJARMAB8CsPm8X2wrpFCRus+DMdDGWQXV3RFc0FbeYFMehn
-46k3+5dB96Y3Wh4cK3/aS2zpR2ddynN6vBaWsSTNfadGbUtIodZgl50ecdyVeExm
-L/H9HWhcafcNJVUeI0jd79Px90puB1auK6fuMVinDv2zJL3HIbz3qUTRAlVHdmph
-f/UoRq0hkZmnbTR0v9eC0FDwJV/XKspicJbv1QIDAQABAoIBAEmUQteLTK0bmoz6
-/wwmVNBVCWxDgMiVgGmkZm/1PrLdDlDk044gPPYTStxRw8usIvbkyGnjAqypTqy0
-p9svA3nspiWfdL9erW3yAs5vhO2D0ooVV1Y8H3Z6i7QEKknpJctf2NDLvO1TYlL7
-l3ox96XaCL3acq85AouQfnffLHM8e0sCj5zc2gDIAz2Vjh5eTR0qIPHNxVL1xeD0
-KGnhTz4WveHraoa3ARzB+fDskilLSdCHrvn8SjaotlFwcSIHVc6ymutBxC/wFIu9
-0O6YamR6b8J1smkVyi+UcIGrXfeLndm3t5jLhmhMJC9D350XhFRLKfoGSTweD1r7
-yGFgDtECgYEA/PRzBeZIcydKQDuFsrItMwrtno0xA9WOJMbKx1QXggX907lJqyZI
-CjeP8GLO7YgXxibHuH8HUC9dy8K33GyZ9YjLkg3f3DZwSUIHwJhKvTbYHDs53oeZ
-4Py5HrDRXYuIZJMpWUCJYEmRsiG1aNL1rTI1V6BrqjUuvu6BXAjKunMCgYEAx6YA
-Ly5i6s7Vcp2/Sm1p+6YhOr0IVjWeJYxJzUYhQKk/EEw/pjsOuVPqH2ikGXnnIOIn
-jveNwfhxEFEL+MU1vEAVOHegDlzP4AdDUSXYV61UN+oJPGRqtByROyR6/s6hbhRr
-HpzOhzndg61N3XzN80pG56UwhLVs1nRjIVRlspcCgYAIkY9D5+UUYYRWYK3Ku9Zk
-ID6kXEwIwTcrb8B2uBaDxQgwH9qq/YT7M56gmfhAe5eykqW5TjRFNxWKTXJE+TjN
-5HBg5i9rGjz7fk0c7Qu7FRyE+EyhiR5hTK5Ip9yvuoZIQePorhL5PAS/b/zhLCQ+
-VbEQ4tJC1cJbnWCsaW/UmQKBgQCH8Xe0qMBAJKBg0BseAcylxuRfi7HuicnqxVDH
-jtY7okLHxTOd7B7FgOctheIfWstPr87B4bzL9HCAbL9bIGXLjlMkxQfeX8JISInE
-6qEaanKrNBgf4Dxr8fvOKrP5ZxeyzgJ2sM1MdNFpxQr9IutVmyEWwHt+Ec9PY6bQ
-Xhh1dwKBgBAGqEz10PnWmJWYylP1wgxNSltS3kGQWP/vqsL7xo4NV0TOYbsu8Iun
-MvLD3C5sSqTD7ycOiweglIFLaZCWtKEp+01WiEsceQ6G9mZ131Zb1uzdp+fmykyi
-IL7R1kM99vpYh0JMj9l8AyNeHG2MKxriOdcDiAOolVxCjYBlnsD0
------END RSA PRIVATE KEY-----
diff --git a/test/lua/testkey.pub b/test/lua/testkey.pub
deleted file mode 100644 (file)
index 6407aa0..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
------BEGIN PUBLIC KEY-----
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxUYMGsqMbNZl4vw65Afi
-vuC5rXDzbvP8zqj96L8t9M/2bV7Df1k4Pit+TKBczhs3HolQStq46AmrhoyNbLJx
-yaA8g+8ETXOhHzQUR74ud/xQaPqx02E02kbR3LnQTp/wdrJARMAB8CsPm8X2wrpF
-CRus+DMdDGWQXV3RFc0FbeYFMehn46k3+5dB96Y3Wh4cK3/aS2zpR2ddynN6vBaW
-sSTNfadGbUtIodZgl50ecdyVeExmL/H9HWhcafcNJVUeI0jd79Px90puB1auK6fu
-MVinDv2zJL3HIbz3qUTRAlVHdmphf/UoRq0hkZmnbTR0v9eC0FDwJV/XKspicJbv
-1QIDAQAB
------END PUBLIC KEY-----
diff --git a/test/lua/tests.lua b/test/lua/tests.lua
new file mode 100644 (file)
index 0000000..0f6b06b
--- /dev/null
@@ -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/unit/rsa.lua b/test/lua/unit/rsa.lua
new file mode 100644 (file)
index 0000000..75d07fd
--- /dev/null
@@ -0,0 +1,45 @@
+-- Test rsa signing
+
+describe("rsa signarture test", function()
+  local rsa_privkey = require "rspamd_rsa_privkey"
+  local rsa_pubkey = require "rspamd_rsa_pubkey"
+  local rsa_signature = require "rspamd_rsa_signature"
+  local rsa = require "rspamd_rsa"
+  local pubkey = 'testkey.pub'
+  local privkey = 'testkey'
+  local data = 'test.data'
+  local signature = 'test.sig'
+
+  -- Signing test
+  local rsa_key = rsa_privkey.load(string.format('%s/%s', test_dir, privkey))
+
+  if not rsa_key then
+    return -1
+  end
+
+  local rsa_sig = rsa.sign_file(rsa_key, string.format('%s/%s', test_dir, data))
+
+  if not rsa_sig then
+    return -1
+  end
+
+  rsa_sig:save(string.format('%s/%s', test_dir, signature), true)
+
+  -- Verifying test
+  rsa_key = rsa_pubkey.load(string.format('%s/%s', test_dir, pubkey))
+
+  if not rsa_key then
+    return -1
+  end
+
+  rsa_sig = rsa_signature.load(string.format('%s/%s', test_dir, signature))
+
+  if not rsa_sig then
+    return -1
+  end
+
+  if not rsa.verify_file(rsa_key, rsa_sig, string.format('%s/%s', test_dir, data)) then
+    return -1
+  end
+
+end)
diff --git a/test/lua/unit/test.data b/test/lua/unit/test.data
new file mode 100644 (file)
index 0000000..05ec159
--- /dev/null
@@ -0,0 +1,19 @@
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas et adipiscing nunc, non auctor justo. Nunc consectetur nunc eget nibh pulvinar, quis consequat tortor mollis. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In eu lectus in mi sodales fermentum. Pellentesque id sapien in augue sodales porta laoreet vel odio. Nam semper massa turpis, sit amet tempus nulla scelerisque non. Sed porttitor sapien est, vitae imperdiet nulla blandit vel.
+
+Proin venenatis neque urna, sit amet elementum risus accumsan in. Nam lectus augue, porttitor a accumsan porta, dignissim ut magna. Cras eleifend orci at eros porttitor posuere. Vestibulum semper accumsan purus, vel varius nisl porta sed. Duis id consectetur ligula, id vestibulum erat. Ut ultricies viverra ipsum sagittis semper. Proin cursus ante at justo pharetra placerat. Donec ultrices eros orci, sed congue libero mattis nec. Ut fringilla quis mauris non condimentum. Proin ante risus, imperdiet et elit vitae, aliquet egestas tortor.
+
+Curabitur tempus, lacus in vehicula pulvinar, odio nisl porta sem, sit amet aliquet metus tellus rhoncus nulla. Morbi condimentum enim vitae lorem imperdiet, ac feugiat nisl suscipit. Proin eleifend laoreet tortor. Sed id risus elementum, sollicitudin nibh et, luctus augue. Cras consequat nec diam in volutpat. Nam tempor nisi nec risus tempus varius quis eget mi. Nunc mauris dolor, imperdiet eget nisi ut, aliquet vulputate massa. Sed consequat diam vitae malesuada vulputate.
+
+Duis ac mollis ante, et suscipit eros. Aenean vel risus nec nisi lacinia aliquam. Phasellus auctor hendrerit facilisis. Etiam at dolor vestibulum, varius quam tempus, adipiscing dolor. Vestibulum erat dui, semper eget orci eget, iaculis laoreet ipsum. Nullam id quam at odio pharetra vestibulum. Morbi porttitor sagittis nunc sed pellentesque. Quisque semper orci ac metus molestie egestas.
+
+Donec mattis commodo magna sit amet pellentesque. In et varius urna. Suspendisse eget tellus nulla. Suspendisse potenti. Praesent a ante sit amet tellus pellentesque ornare id sit amet eros. Donec sed dignissim ligula, nec condimentum nulla. Etiam fermentum leo sit amet tincidunt fringilla. In semper lacus sit amet laoreet volutpat. Curabitur ultricies sem in dui eleifend, at aliquam sapien adipiscing. Aliquam erat volutpat. Maecenas felis eros, posuere quis placerat a, imperdiet ac lectus. Phasellus volutpat lorem sed nulla vehicula pulvinar. Pellentesque ipsum ante, posuere id eros non, vehicula commodo augue. Proin vitae tempor lectus, at commodo elit. Donec luctus commodo erat. Curabitur faucibus, arcu tincidunt vehicula rutrum, quam turpis hendrerit sapien, nec vehicula est est ut turpis.
+
+Suspendisse elementum ligula nibh. Nullam interdum tempor metus sed ultrices. Proin nec venenatis tellus. Donec non tortor at ipsum ultrices sagittis. Integer lectus augue, lacinia quis ante iaculis, iaculis volutpat odio. Fusce porttitor quis nisi vitae condimentum. Nam luctus placerat fringilla. Praesent vulputate aliquet convallis. Nunc vel sagittis sem. Vestibulum vitae ante a ipsum pulvinar faucibus pulvinar ac nulla. Morbi mattis nulla et dui egestas, vitae volutpat nisi pretium. Ut ullamcorper ac orci nec lacinia. Proin ac condimentum diam. Nulla blandit, tellus sit amet volutpat suscipit, purus eros iaculis odio, in euismod nulla nisl at urna. Aliquam erat volutpat. Pellentesque hendrerit dui eget lobortis congue.
+
+Etiam condimentum adipiscing sollicitudin. Curabitur at rhoncus sem. Donec dignissim, erat sodales malesuada fringilla, metus est vehicula nulla, nec volutpat lacus neque ut libero. Suspendisse potenti. Nam vel facilisis massa. Morbi eget quam urna. Suspendisse quis lacus eget justo placerat pulvinar sit amet a nunc. Cras porta magna nec risus feugiat, pharetra rhoncus mauris convallis. Integer pretium dolor massa, a consectetur velit laoreet sed. Nunc rutrum convallis orci, sit amet congue sapien iaculis at.
+
+Etiam leo nibh, ultricies eget dictum non, euismod nec quam. Cras luctus mauris odio, in dapibus risus rhoncus et. Integer ullamcorper elit neque. Nam vel nisl sit amet nunc gravida laoreet. Suspendisse sed neque id nulla tempus laoreet. Aliquam interdum lorem quis erat lacinia, in congue velit euismod. Morbi sit amet viverra dolor, lacinia pretium massa. Proin eu rutrum augue, sit amet condimentum enim. Pellentesque sodales, justo quis gravida lacinia, nulla dolor accumsan sapien, non hendrerit urna dui quis nibh. In posuere non elit eu hendrerit.
+
+Nulla eget justo ultrices tellus tristique cursus. Maecenas in sem et tellus pulvinar rhoncus quis ac urna. Vestibulum imperdiet nisl nibh, at suscipit mi varius eu. Vestibulum adipiscing tellus sed ipsum vehicula, vitae convallis felis ornare. Sed commodo varius adipiscing. Pellentesque eget magna diam. Etiam ac ante ligula. Proin mattis quis odio id placerat. Nam luctus, sapien rhoncus iaculis elementum, diam magna condimentum velit, eu pellentesque orci tortor eget dui. Fusce adipiscing feugiat nunc, ac iaculis lorem ultrices sit amet. Proin vel felis eget nulla consectetur congue vel sed sapien. Morbi sed aliquam neque. Interdum et malesuada fames ac ante ipsum primis in faucibus. In volutpat velit ut est sodales, eget porttitor massa faucibus.
+
+Nunc vel fermentum augue, vitae convallis metus. Pellentesque accumsan ipsum nec condimentum pharetra. Quisque id nisi egestas, ultrices mi nec, faucibus orci. Aenean aliquam, ipsum lobortis tristique facilisis, neque purus commodo est, semper semper nunc metus quis ligula. Praesent faucibus est vel quam eleifend, a accumsan diam tempus. Integer pellentesque rhoncus vehicula. Donec sit amet mollis sem. Vivamus et ipsum at justo iaculis imperdiet a vel felis. Morbi felis augue, condimentum sit amet ligula et, mollis adipiscing purus. In vulputate nibh nec mattis fermentum. In ornare felis in aliquam cursus. Suspendisse magna mi, vehicula ut egestas eu, egestas non eros. Nunc non sem massa. Pellentesque mattis, lacus sit amet luctus scelerisque, lacus est ultricies erat, ut tincidunt nisl orci id leo. Maecenas ut massa volutpat, aliquet odio placerat, luctus massa. Ut sed odio tincidunt, luctus arcu in, dictum leo. 
diff --git a/test/lua/unit/testkey b/test/lua/unit/testkey
new file mode 100644 (file)
index 0000000..4a0325b
--- /dev/null
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAxUYMGsqMbNZl4vw65AfivuC5rXDzbvP8zqj96L8t9M/2bV7D
+f1k4Pit+TKBczhs3HolQStq46AmrhoyNbLJxyaA8g+8ETXOhHzQUR74ud/xQaPqx
+02E02kbR3LnQTp/wdrJARMAB8CsPm8X2wrpFCRus+DMdDGWQXV3RFc0FbeYFMehn
+46k3+5dB96Y3Wh4cK3/aS2zpR2ddynN6vBaWsSTNfadGbUtIodZgl50ecdyVeExm
+L/H9HWhcafcNJVUeI0jd79Px90puB1auK6fuMVinDv2zJL3HIbz3qUTRAlVHdmph
+f/UoRq0hkZmnbTR0v9eC0FDwJV/XKspicJbv1QIDAQABAoIBAEmUQteLTK0bmoz6
+/wwmVNBVCWxDgMiVgGmkZm/1PrLdDlDk044gPPYTStxRw8usIvbkyGnjAqypTqy0
+p9svA3nspiWfdL9erW3yAs5vhO2D0ooVV1Y8H3Z6i7QEKknpJctf2NDLvO1TYlL7
+l3ox96XaCL3acq85AouQfnffLHM8e0sCj5zc2gDIAz2Vjh5eTR0qIPHNxVL1xeD0
+KGnhTz4WveHraoa3ARzB+fDskilLSdCHrvn8SjaotlFwcSIHVc6ymutBxC/wFIu9
+0O6YamR6b8J1smkVyi+UcIGrXfeLndm3t5jLhmhMJC9D350XhFRLKfoGSTweD1r7
+yGFgDtECgYEA/PRzBeZIcydKQDuFsrItMwrtno0xA9WOJMbKx1QXggX907lJqyZI
+CjeP8GLO7YgXxibHuH8HUC9dy8K33GyZ9YjLkg3f3DZwSUIHwJhKvTbYHDs53oeZ
+4Py5HrDRXYuIZJMpWUCJYEmRsiG1aNL1rTI1V6BrqjUuvu6BXAjKunMCgYEAx6YA
+Ly5i6s7Vcp2/Sm1p+6YhOr0IVjWeJYxJzUYhQKk/EEw/pjsOuVPqH2ikGXnnIOIn
+jveNwfhxEFEL+MU1vEAVOHegDlzP4AdDUSXYV61UN+oJPGRqtByROyR6/s6hbhRr
+HpzOhzndg61N3XzN80pG56UwhLVs1nRjIVRlspcCgYAIkY9D5+UUYYRWYK3Ku9Zk
+ID6kXEwIwTcrb8B2uBaDxQgwH9qq/YT7M56gmfhAe5eykqW5TjRFNxWKTXJE+TjN
+5HBg5i9rGjz7fk0c7Qu7FRyE+EyhiR5hTK5Ip9yvuoZIQePorhL5PAS/b/zhLCQ+
+VbEQ4tJC1cJbnWCsaW/UmQKBgQCH8Xe0qMBAJKBg0BseAcylxuRfi7HuicnqxVDH
+jtY7okLHxTOd7B7FgOctheIfWstPr87B4bzL9HCAbL9bIGXLjlMkxQfeX8JISInE
+6qEaanKrNBgf4Dxr8fvOKrP5ZxeyzgJ2sM1MdNFpxQr9IutVmyEWwHt+Ec9PY6bQ
+Xhh1dwKBgBAGqEz10PnWmJWYylP1wgxNSltS3kGQWP/vqsL7xo4NV0TOYbsu8Iun
+MvLD3C5sSqTD7ycOiweglIFLaZCWtKEp+01WiEsceQ6G9mZ131Zb1uzdp+fmykyi
+IL7R1kM99vpYh0JMj9l8AyNeHG2MKxriOdcDiAOolVxCjYBlnsD0
+-----END RSA PRIVATE KEY-----
diff --git a/test/lua/unit/testkey.pub b/test/lua/unit/testkey.pub
new file mode 100644 (file)
index 0000000..6407aa0
--- /dev/null
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxUYMGsqMbNZl4vw65Afi
+vuC5rXDzbvP8zqj96L8t9M/2bV7Df1k4Pit+TKBczhs3HolQStq46AmrhoyNbLJx
+yaA8g+8ETXOhHzQUR74ud/xQaPqx02E02kbR3LnQTp/wdrJARMAB8CsPm8X2wrpF
+CRus+DMdDGWQXV3RFc0FbeYFMehn46k3+5dB96Y3Wh4cK3/aS2zpR2ddynN6vBaW
+sSTNfadGbUtIodZgl50ecdyVeExmL/H9HWhcafcNJVUeI0jd79Px90puB1auK6fu
+MVinDv2zJL3HIbz3qUTRAlVHdmphf/UoRq0hkZmnbTR0v9eC0FDwJV/XKspicJbv
+1QIDAQAB
+-----END PUBLIC KEY-----
index cd3b152cda9c46849ee6cdba4350ca3e32069d01..bdc23e91ca7400727193bbb7acb54ba700f4bc6a 100644 (file)
 #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);
        }
 
index a70a0102f9d057d08363160eebc0781d508cf910..1023c5cf0aa27e561b6fd477dcd7d56749e7e898 100644 (file)
@@ -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 ();
 
index ae69d9d96c362c71a3f933b56c4f226d1e02eba3..1d14bde4bb689e8462e2af9e70e9610886d56bc7 100644 (file)
@@ -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