BLI_expr_pylike_eval_test.cc 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. /* Apache License, Version 2.0 */
  2. #include "testing/testing.h"
  3. #include <string.h>
  4. extern "C" {
  5. #include "BLI_expr_pylike_eval.h"
  6. #include "BLI_math.h"
  7. };
  8. #define TRUE_VAL 1.0
  9. #define FALSE_VAL 0.0
  10. static void expr_pylike_parse_fail_test(const char *str)
  11. {
  12. ExprPyLike_Parsed *expr = BLI_expr_pylike_parse(str, NULL, 0);
  13. EXPECT_FALSE(BLI_expr_pylike_is_valid(expr));
  14. BLI_expr_pylike_free(expr);
  15. }
  16. static void expr_pylike_const_test(const char *str, double value, bool force_const)
  17. {
  18. ExprPyLike_Parsed *expr = BLI_expr_pylike_parse(str, NULL, 0);
  19. if (force_const) {
  20. EXPECT_TRUE(BLI_expr_pylike_is_constant(expr));
  21. }
  22. else {
  23. EXPECT_TRUE(BLI_expr_pylike_is_valid(expr));
  24. EXPECT_FALSE(BLI_expr_pylike_is_constant(expr));
  25. }
  26. double result;
  27. eExprPyLike_EvalStatus status = BLI_expr_pylike_eval(expr, NULL, 0, &result);
  28. EXPECT_EQ(status, EXPR_PYLIKE_SUCCESS);
  29. EXPECT_EQ(result, value);
  30. BLI_expr_pylike_free(expr);
  31. }
  32. static ExprPyLike_Parsed *parse_for_eval(const char *str, bool nonconst)
  33. {
  34. const char *names[1] = {"x"};
  35. ExprPyLike_Parsed *expr = BLI_expr_pylike_parse(str, names, ARRAY_SIZE(names));
  36. EXPECT_TRUE(BLI_expr_pylike_is_valid(expr));
  37. if (nonconst) {
  38. EXPECT_FALSE(BLI_expr_pylike_is_constant(expr));
  39. }
  40. return expr;
  41. }
  42. static void verify_eval_result(ExprPyLike_Parsed *expr, double x, double value)
  43. {
  44. double result;
  45. eExprPyLike_EvalStatus status = BLI_expr_pylike_eval(expr, &x, 1, &result);
  46. EXPECT_EQ(status, EXPR_PYLIKE_SUCCESS);
  47. EXPECT_EQ(result, value);
  48. }
  49. static void expr_pylike_eval_test(const char *str, double x, double value)
  50. {
  51. ExprPyLike_Parsed *expr = parse_for_eval(str, true);
  52. verify_eval_result(expr, x, value);
  53. BLI_expr_pylike_free(expr);
  54. }
  55. static void expr_pylike_error_test(const char *str, double x, eExprPyLike_EvalStatus error)
  56. {
  57. ExprPyLike_Parsed *expr = parse_for_eval(str, false);
  58. double result;
  59. eExprPyLike_EvalStatus status = BLI_expr_pylike_eval(expr, &x, 1, &result);
  60. EXPECT_EQ(status, error);
  61. BLI_expr_pylike_free(expr);
  62. }
  63. #define TEST_PARSE_FAIL(name, str) \
  64. TEST(expr_pylike, ParseFail_##name) \
  65. { \
  66. expr_pylike_parse_fail_test(str); \
  67. }
  68. TEST_PARSE_FAIL(Empty, "")
  69. TEST_PARSE_FAIL(ConstHex, "0x0")
  70. TEST_PARSE_FAIL(ConstOctal, "01")
  71. TEST_PARSE_FAIL(Tail, "0 0")
  72. TEST_PARSE_FAIL(ConstFloatExp, "0.5e+")
  73. TEST_PARSE_FAIL(BadId, "Pi")
  74. TEST_PARSE_FAIL(BadArgCount0, "sqrt")
  75. TEST_PARSE_FAIL(BadArgCount1, "sqrt()")
  76. TEST_PARSE_FAIL(BadArgCount2, "sqrt(1,2)")
  77. TEST_PARSE_FAIL(BadArgCount3, "pi()")
  78. TEST_PARSE_FAIL(BadArgCount4, "max()")
  79. TEST_PARSE_FAIL(BadArgCount5, "min()")
  80. TEST_PARSE_FAIL(Truncated1, "(1+2")
  81. TEST_PARSE_FAIL(Truncated2, "1 if 2")
  82. TEST_PARSE_FAIL(Truncated3, "1 if 2 else")
  83. TEST_PARSE_FAIL(Truncated4, "1 < 2 <")
  84. TEST_PARSE_FAIL(Truncated5, "1 +")
  85. TEST_PARSE_FAIL(Truncated6, "1 *")
  86. TEST_PARSE_FAIL(Truncated7, "1 and")
  87. TEST_PARSE_FAIL(Truncated8, "1 or")
  88. TEST_PARSE_FAIL(Truncated9, "sqrt(1")
  89. TEST_PARSE_FAIL(Truncated10, "fmod(1,")
  90. /* Constant expression with working constant folding */
  91. #define TEST_CONST(name, str, value) \
  92. TEST(expr_pylike, Const_##name) \
  93. { \
  94. expr_pylike_const_test(str, value, true); \
  95. }
  96. /* Constant expression but constant folding is not supported */
  97. #define TEST_RESULT(name, str, value) \
  98. TEST(expr_pylike, Result_##name) \
  99. { \
  100. expr_pylike_const_test(str, value, false); \
  101. }
  102. /* Expression with an argument */
  103. #define TEST_EVAL(name, str, x, value) \
  104. TEST(expr_pylike, Eval_##name) \
  105. { \
  106. expr_pylike_eval_test(str, x, value); \
  107. }
  108. TEST_CONST(Zero, "0", 0.0)
  109. TEST_CONST(Zero2, "00", 0.0)
  110. TEST_CONST(One, "1", 1.0)
  111. TEST_CONST(OneF, "1.0", 1.0)
  112. TEST_CONST(OneF2, "1.", 1.0)
  113. TEST_CONST(OneE, "1e0", 1.0)
  114. TEST_CONST(TenE, "1.e+1", 10.0)
  115. TEST_CONST(Half, ".5", 0.5)
  116. TEST_CONST(Pi, "pi", M_PI)
  117. TEST_CONST(True, "True", TRUE_VAL)
  118. TEST_CONST(False, "False", FALSE_VAL)
  119. TEST_CONST(Sqrt, "sqrt(4)", 2.0)
  120. TEST_EVAL(Sqrt, "sqrt(x)", 4.0, 2.0)
  121. TEST_CONST(FMod, "fmod(3.5, 2)", 1.5)
  122. TEST_EVAL(FMod, "fmod(x, 2)", 3.5, 1.5)
  123. TEST_CONST(Pow, "pow(4, 0.5)", 2.0)
  124. TEST_EVAL(Pow, "pow(4, x)", 0.5, 2.0)
  125. TEST_RESULT(Min1, "min(3,1,2)", 1.0)
  126. TEST_RESULT(Max1, "max(3,1,2)", 3.0)
  127. TEST_RESULT(Min2, "min(1,2,3)", 1.0)
  128. TEST_RESULT(Max2, "max(1,2,3)", 3.0)
  129. TEST_RESULT(Min3, "min(2,3,1)", 1.0)
  130. TEST_RESULT(Max3, "max(2,3,1)", 3.0)
  131. TEST_CONST(UnaryPlus, "+1", 1.0)
  132. TEST_CONST(UnaryMinus, "-1", -1.0)
  133. TEST_EVAL(UnaryMinus, "-x", 1.0, -1.0)
  134. TEST_CONST(BinaryPlus, "1+2", 3.0)
  135. TEST_EVAL(BinaryPlus, "x+2", 1, 3.0)
  136. TEST_CONST(BinaryMinus, "1-2", -1.0)
  137. TEST_EVAL(BinaryMinus, "1-x", 2, -1.0)
  138. TEST_CONST(BinaryMul, "2*3", 6.0)
  139. TEST_EVAL(BinaryMul, "x*3", 2, 6.0)
  140. TEST_CONST(BinaryDiv, "3/2", 1.5)
  141. TEST_EVAL(BinaryDiv, "3/x", 2, 1.5)
  142. TEST_CONST(Arith1, "1 + -2 * 3", -5.0)
  143. TEST_CONST(Arith2, "(1 + -2) * 3", -3.0)
  144. TEST_CONST(Arith3, "-1 + 2 * 3", 5.0)
  145. TEST_CONST(Arith4, "3 * (-2 + 1)", -3.0)
  146. TEST_EVAL(Arith1, "1 + -x * 3", 2, -5.0)
  147. TEST_CONST(Eq1, "1 == 1.0", TRUE_VAL)
  148. TEST_CONST(Eq2, "1 == 2.0", FALSE_VAL)
  149. TEST_CONST(Eq3, "True == 1", TRUE_VAL)
  150. TEST_CONST(Eq4, "False == 0", TRUE_VAL)
  151. TEST_EVAL(Eq1, "1 == x", 1.0, TRUE_VAL)
  152. TEST_EVAL(Eq2, "1 == x", 2.0, FALSE_VAL)
  153. TEST_CONST(NEq1, "1 != 1.0", FALSE_VAL)
  154. TEST_CONST(NEq2, "1 != 2.0", TRUE_VAL)
  155. TEST_EVAL(NEq1, "1 != x", 1.0, FALSE_VAL)
  156. TEST_EVAL(NEq2, "1 != x", 2.0, TRUE_VAL)
  157. TEST_CONST(Lt1, "1 < 1", FALSE_VAL)
  158. TEST_CONST(Lt2, "1 < 2", TRUE_VAL)
  159. TEST_CONST(Lt3, "2 < 1", FALSE_VAL)
  160. TEST_CONST(Le1, "1 <= 1", TRUE_VAL)
  161. TEST_CONST(Le2, "1 <= 2", TRUE_VAL)
  162. TEST_CONST(Le3, "2 <= 1", FALSE_VAL)
  163. TEST_CONST(Gt1, "1 > 1", FALSE_VAL)
  164. TEST_CONST(Gt2, "1 > 2", FALSE_VAL)
  165. TEST_CONST(Gt3, "2 > 1", TRUE_VAL)
  166. TEST_CONST(Ge1, "1 >= 1", TRUE_VAL)
  167. TEST_CONST(Ge2, "1 >= 2", FALSE_VAL)
  168. TEST_CONST(Ge3, "2 >= 1", TRUE_VAL)
  169. TEST_CONST(Cmp1, "3 == 1 + 2", TRUE_VAL)
  170. TEST_EVAL(Cmp1, "3 == x + 2", 1, TRUE_VAL)
  171. TEST_EVAL(Cmp1b, "3 == x + 2", 1.5, FALSE_VAL)
  172. TEST_RESULT(CmpChain1, "1 < 2 < 3", TRUE_VAL)
  173. TEST_RESULT(CmpChain2, "1 < 2 == 2", TRUE_VAL)
  174. TEST_RESULT(CmpChain3, "1 < 2 > -1", TRUE_VAL)
  175. TEST_RESULT(CmpChain4, "1 < 2 < 2 < 3", FALSE_VAL)
  176. TEST_RESULT(CmpChain5, "1 < 2 <= 2 < 3", TRUE_VAL)
  177. TEST_EVAL(CmpChain1a, "1 < x < 3", 2, TRUE_VAL)
  178. TEST_EVAL(CmpChain1b, "1 < x < 3", 1, FALSE_VAL)
  179. TEST_EVAL(CmpChain1c, "1 < x < 3", 3, FALSE_VAL)
  180. TEST_CONST(Not1, "not 2", FALSE_VAL)
  181. TEST_CONST(Not2, "not 0", TRUE_VAL)
  182. TEST_CONST(Not3, "not not 2", TRUE_VAL)
  183. TEST_EVAL(Not1, "not x", 2, FALSE_VAL)
  184. TEST_EVAL(Not2, "not x", 0, TRUE_VAL)
  185. TEST_RESULT(And1, "2 and 3", 3.0)
  186. TEST_RESULT(And2, "0 and 3", 0.0)
  187. TEST_RESULT(Or1, "2 or 3", 2.0)
  188. TEST_RESULT(Or2, "0 or 3", 3.0)
  189. TEST_RESULT(Bool1, "2 or 3 and 4", 2.0)
  190. TEST_RESULT(Bool2, "not 2 or 3 and 4", 4.0)
  191. TEST(expr_pylike, Eval_Ternary1)
  192. {
  193. ExprPyLike_Parsed *expr = parse_for_eval("x / 2 if x < 4 else x - 2 if x < 8 else x*2 - 12",
  194. true);
  195. for (int i = 0; i <= 10; i++) {
  196. double x = i;
  197. double v = (x < 4) ? (x / 2) : (x < 8) ? (x - 2) : (x * 2 - 12);
  198. verify_eval_result(expr, x, v);
  199. }
  200. BLI_expr_pylike_free(expr);
  201. }
  202. TEST(expr_pylike, MultipleArgs)
  203. {
  204. const char *names[3] = {"x", "y", "x"};
  205. double values[3] = {1.0, 2.0, 3.0};
  206. ExprPyLike_Parsed *expr = BLI_expr_pylike_parse("x*10 + y", names, ARRAY_SIZE(names));
  207. EXPECT_TRUE(BLI_expr_pylike_is_valid(expr));
  208. double result;
  209. eExprPyLike_EvalStatus status = BLI_expr_pylike_eval(expr, values, 3, &result);
  210. EXPECT_EQ(status, EXPR_PYLIKE_SUCCESS);
  211. EXPECT_EQ(result, 32.0);
  212. BLI_expr_pylike_free(expr);
  213. }
  214. #define TEST_ERROR(name, str, x, code) \
  215. TEST(expr_pylike, Error_##name) \
  216. { \
  217. expr_pylike_error_test(str, x, code); \
  218. }
  219. TEST_ERROR(DivZero1, "0 / 0", 0.0, EXPR_PYLIKE_MATH_ERROR)
  220. TEST_ERROR(DivZero2, "1 / 0", 0.0, EXPR_PYLIKE_DIV_BY_ZERO)
  221. TEST_ERROR(DivZero3, "1 / x", 0.0, EXPR_PYLIKE_DIV_BY_ZERO)
  222. TEST_ERROR(DivZero4, "1 / x", 1.0, EXPR_PYLIKE_SUCCESS)
  223. TEST_ERROR(SqrtDomain1, "sqrt(-1)", 0.0, EXPR_PYLIKE_MATH_ERROR)
  224. TEST_ERROR(SqrtDomain2, "sqrt(x)", -1.0, EXPR_PYLIKE_MATH_ERROR)
  225. TEST_ERROR(SqrtDomain3, "sqrt(x)", 0.0, EXPR_PYLIKE_SUCCESS)
  226. TEST_ERROR(PowDomain1, "pow(-1, 0.5)", 0.0, EXPR_PYLIKE_MATH_ERROR)
  227. TEST_ERROR(PowDomain2, "pow(-1, x)", 0.5, EXPR_PYLIKE_MATH_ERROR)
  228. TEST_ERROR(PowDomain3, "pow(-1, x)", 2.0, EXPR_PYLIKE_SUCCESS)
  229. TEST_ERROR(Mixed1, "sqrt(x) + 1 / max(0, x)", -1.0, EXPR_PYLIKE_MATH_ERROR)
  230. TEST_ERROR(Mixed2, "sqrt(x) + 1 / max(0, x)", 0.0, EXPR_PYLIKE_DIV_BY_ZERO)
  231. TEST_ERROR(Mixed3, "sqrt(x) + 1 / max(0, x)", 1.0, EXPR_PYLIKE_SUCCESS)
  232. TEST(expr_pylike, Error_Invalid)
  233. {
  234. ExprPyLike_Parsed *expr = BLI_expr_pylike_parse("", NULL, 0);
  235. double result;
  236. EXPECT_EQ(BLI_expr_pylike_eval(expr, NULL, 0, &result), EXPR_PYLIKE_INVALID);
  237. BLI_expr_pylike_free(expr);
  238. }
  239. TEST(expr_pylike, Error_ArgumentCount)
  240. {
  241. ExprPyLike_Parsed *expr = parse_for_eval("x", false);
  242. double result;
  243. EXPECT_EQ(BLI_expr_pylike_eval(expr, NULL, 0, &result), EXPR_PYLIKE_FATAL_ERROR);
  244. BLI_expr_pylike_free(expr);
  245. }