1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
|
-- Expressions unit tests
context("Rspamd expressions", function()
local rspamd_expression = require "rspamd_expression"
local rspamd_mempool = require "rspamd_mempool"
local rspamd_regexp = require "rspamd_regexp"
local split_re = rspamd_regexp.create('/\\s+|\\)|\\(/')
local function parse_func(str)
-- extract token till the first space character
local token = str
local t = split_re:split(str)
if t then
token = t[1]
end
-- Return token name
return token
end
local atoms = {
A = 1.0,
B = 0,
C = 1,
D = 0,
E = 1,
F = 0,
G = 0,
H = 0,
I = 0,
J = 0,
K = 0,
}
local function process_func(token, input)
--print(token)
local t = input[token]
return t
end
local pool = rspamd_mempool.create()
local cases = {
{'A & (!B | C)', '(A) (B) ! (C) | &'},
{'A & B | !C', '(C) ! (A) (B) & |'},
{'A & (B | !C)', '(A) (B) (C) ! | &'},
{'A & B &', nil},
-- Unbalanced braces
{'(((A))', nil},
-- Balanced braces
{'(((A)))', '(A)'},
-- Plus and comparison operators
{'A + B + C + D > 2', '(A) (B) (C) (D) +(4) 2 >'},
-- Plus and logic operators
{'((A + B + C + D) > 2) & D', '(D) (A) (B) (C) (D) +(4) 2 > &'},
-- Associativity
{'A | B | C & D & E', '(A) (B) (C) (D) (E) &(3) |(3)'},
-- More associativity
{'1 | 0 & 0 | 0', '(1) (0) (0) (0) & |(3)'},
{'(A) & (B) & ((C) | (D) | (E) | (F))', '(A) (B) (C) (D) (E) (F) |(4) &(3)' },
-- Extra space
{'A & B | ! C', '(C) ! (A) (B) & |'},
-- False minus
{'A + B + -C', '(A) (B) (-C) +(3)'},
}
for _,c in ipairs(cases) do
test("Expression creation function: " .. c[1], function()
local expr,err = rspamd_expression.create(c[1],
{parse_func, process_func}, pool)
if not c[2] then
assert_nil(expr, "Should not be able to parse " .. c[1])
else
assert_not_nil(expr, "Cannot parse " .. c[1] .. '; error: ' .. (err or 'wut??'))
assert_equal(expr:to_string(), c[2], string.format("Evaluated expr to '%s', expected: '%s'",
expr:to_string(), c[2]))
end
end)
end
-- Expression is destroyed when the corresponding pool is destroyed
cases = {
{'(E) && ((B + B + B + B) >= 1)', 0},
{'A & B | !C', 0},
{'A & (!B | C)', 1},
{'A + B + C + D + E + F >= 2', 1},
{'((A + B + C + D) > 1) & F', 0},
{'(A + B + C + D) > 1 && F || E', 1},
{'(A + B + C + D) > 100 && F || !E', 0},
{'F && ((A + B + C + D) > 1)', 0},
{'(E) && ((B + B + B + B) >= 1)', 0},
{'!!C', 1},
{'(B) & (D) & ((G) | (H) | (I) | (A))', 0},
{'A & C & (!D || !C || !E)', 1},
{'A & C & !(D || C || E)', 0},
{'A + B + C', 2},
{'A * 2.0 + B + C', 3},
{'A * 2.0 + B - C', 1},
{'A / 2.0 + B - C', -0.5},
}
for _,c in ipairs(cases) do
test("Expression process function: " .. c[1], function()
local expr,err = rspamd_expression.create(c[1],
{parse_func, process_func}, pool)
assert_not_nil(expr, "Cannot parse " .. c[1] .. '; error: ' .. (err or 'wut??'))
res = expr:process(atoms)
assert_equal(res, c[2], string.format("Processed expr '%s'{%s} returned '%d', expected: '%d'",
expr:to_string(), c[1], res, c[2]))
end)
end
end)
|