You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

expressions.lua 3.3KB

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