Blender  V3.3
BLI_expr_pylike_eval_test.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: Apache-2.0 */
2 
3 #include "testing/testing.h"
4 
5 #include <cstring>
6 
7 #include "BLI_expr_pylike_eval.h"
8 #include "BLI_math.h"
9 
10 #define TRUE_VAL 1.0
11 #define FALSE_VAL 0.0
12 
13 static void expr_pylike_parse_fail_test(const char *str)
14 {
15  ExprPyLike_Parsed *expr = BLI_expr_pylike_parse(str, nullptr, 0);
16 
17  EXPECT_FALSE(BLI_expr_pylike_is_valid(expr));
18 
20 }
21 
22 static void expr_pylike_const_test(const char *str, double value, bool force_const)
23 {
24  ExprPyLike_Parsed *expr = BLI_expr_pylike_parse(str, nullptr, 0);
25 
26  if (force_const) {
27  EXPECT_TRUE(BLI_expr_pylike_is_constant(expr));
28  }
29  else {
30  EXPECT_TRUE(BLI_expr_pylike_is_valid(expr));
31  EXPECT_FALSE(BLI_expr_pylike_is_constant(expr));
32  }
33 
34  double result;
35  eExprPyLike_EvalStatus status = BLI_expr_pylike_eval(expr, nullptr, 0, &result);
36 
38  EXPECT_EQ(result, value);
39 
41 }
42 
43 static ExprPyLike_Parsed *parse_for_eval(const char *str, bool nonconst)
44 {
45  const char *names[1] = {"x"};
47 
48  EXPECT_TRUE(BLI_expr_pylike_is_valid(expr));
49 
50  if (nonconst) {
51  EXPECT_FALSE(BLI_expr_pylike_is_constant(expr));
52  }
53 
54  return expr;
55 }
56 
57 static void verify_eval_result(ExprPyLike_Parsed *expr, double x, double value)
58 {
59  double result;
61 
63  EXPECT_EQ(result, value);
64 }
65 
66 static void expr_pylike_eval_test(const char *str, double x, double value)
67 {
68  ExprPyLike_Parsed *expr = parse_for_eval(str, true);
69  verify_eval_result(expr, x, value);
71 }
72 
73 static void expr_pylike_error_test(const char *str, double x, eExprPyLike_EvalStatus error)
74 {
75  ExprPyLike_Parsed *expr = parse_for_eval(str, false);
76 
77  double result;
79 
80  EXPECT_EQ(status, error);
81 
83 }
84 
85 #define TEST_PARSE_FAIL(name, str) \
86  TEST(expr_pylike, ParseFail_##name) \
87  { \
88  expr_pylike_parse_fail_test(str); \
89  }
90 
91 TEST_PARSE_FAIL(Empty, "")
92 TEST_PARSE_FAIL(ConstHex, "0x0")
93 TEST_PARSE_FAIL(ConstOctal, "01")
94 TEST_PARSE_FAIL(Tail, "0 0")
95 TEST_PARSE_FAIL(ConstFloatExp, "0.5e+")
96 TEST_PARSE_FAIL(BadId, "Pi")
97 TEST_PARSE_FAIL(BadArgCount0, "sqrt")
98 TEST_PARSE_FAIL(BadArgCount1, "sqrt()")
99 TEST_PARSE_FAIL(BadArgCount2, "sqrt(1,2)")
100 TEST_PARSE_FAIL(BadArgCount3, "pi()")
101 TEST_PARSE_FAIL(BadArgCount4, "max()")
102 TEST_PARSE_FAIL(BadArgCount5, "min()")
103 
104 TEST_PARSE_FAIL(Truncated1, "(1+2")
105 TEST_PARSE_FAIL(Truncated2, "1 if 2")
106 TEST_PARSE_FAIL(Truncated3, "1 if 2 else")
107 TEST_PARSE_FAIL(Truncated4, "1 < 2 <")
108 TEST_PARSE_FAIL(Truncated5, "1 +")
109 TEST_PARSE_FAIL(Truncated6, "1 *")
110 TEST_PARSE_FAIL(Truncated7, "1 and")
111 TEST_PARSE_FAIL(Truncated8, "1 or")
112 TEST_PARSE_FAIL(Truncated9, "sqrt(1")
113 TEST_PARSE_FAIL(Truncated10, "fmod(1,")
114 
115 /* Constant expression with working constant folding */
116 #define TEST_CONST(name, str, value) \
117  TEST(expr_pylike, Const_##name) \
118  { \
119  expr_pylike_const_test(str, value, true); \
120  }
121 
122 /* Constant expression but constant folding is not supported */
123 #define TEST_RESULT(name, str, value) \
124  TEST(expr_pylike, Result_##name) \
125  { \
126  expr_pylike_const_test(str, value, false); \
127  }
128 
129 /* Expression with an argument */
130 #define TEST_EVAL(name, str, x, value) \
131  TEST(expr_pylike, Eval_##name) \
132  { \
133  expr_pylike_eval_test(str, x, value); \
134  }
135 
136 TEST_CONST(Zero, "0", 0.0)
137 TEST_CONST(Zero2, "00", 0.0)
138 TEST_CONST(One, "1", 1.0)
139 TEST_CONST(OneF, "1.0", 1.0)
140 TEST_CONST(OneF2, "1.", 1.0)
141 TEST_CONST(OneE, "1e0", 1.0)
142 TEST_CONST(TenE, "1.e+1", 10.0)
143 TEST_CONST(Half, ".5", 0.5)
144 
145 TEST_CONST(Pi, "pi", M_PI)
146 TEST_CONST(True, "True", TRUE_VAL)
147 TEST_CONST(False, "False", FALSE_VAL)
148 
149 TEST_CONST(Sqrt, "sqrt(4)", 2.0)
150 TEST_EVAL(Sqrt, "sqrt(x)", 4.0, 2.0)
151 
152 TEST_CONST(FMod, "fmod(3.5, 2)", 1.5)
153 TEST_EVAL(FMod, "fmod(x, 2)", 3.5, 1.5)
154 
155 TEST_CONST(Pow, "pow(4, 0.5)", 2.0)
156 TEST_EVAL(Pow, "pow(4, x)", 0.5, 2.0)
157 
158 TEST_CONST(Log2_1, "log(4, 2)", 2.0)
159 
160 TEST_CONST(Round1, "round(-0.5)", -1.0)
161 TEST_CONST(Round2, "round(-0.4)", 0.0)
162 TEST_CONST(Round3, "round(0.4)", 0.0)
163 TEST_CONST(Round4, "round(0.5)", 1.0)
164 
165 TEST_CONST(Clamp1, "clamp(-0.1)", 0.0)
166 TEST_CONST(Clamp2, "clamp(0.5)", 0.5)
167 TEST_CONST(Clamp3, "clamp(1.5)", 1.0)
168 TEST_CONST(Clamp4, "clamp(0.5, 0.2, 0.3)", 0.3)
169 TEST_CONST(Clamp5, "clamp(0.0, 0.2, 0.3)", 0.2)
170 
171 TEST_CONST(Lerp1, "lerp(-10,10,-1)", -30.0)
172 TEST_CONST(Lerp2, "lerp(-10,10,0.25)", -5.0)
173 TEST_CONST(Lerp3, "lerp(-10,10,1)", 10.0)
174 TEST_EVAL(Lerp1, "lerp(-10,10,x)", 0, -10.0)
175 TEST_EVAL(Lerp2, "lerp(-10,10,x)", 0.75, 5.0)
176 
177 TEST_CONST(Smoothstep1, "smoothstep(-10,10,-20)", 0.0)
178 TEST_CONST(Smoothstep2, "smoothstep(-10,10,-10)", 0.0)
179 TEST_CONST(Smoothstep3, "smoothstep(-10,10,10)", 1.0)
180 TEST_CONST(Smoothstep4, "smoothstep(-10,10,20)", 1.0)
181 TEST_CONST(Smoothstep5, "smoothstep(-10,10,-5)", 0.15625)
182 TEST_EVAL(Smoothstep1, "smoothstep(-10,10,x)", 5, 0.84375)
183 
184 TEST_RESULT(Min1, "min(3,1,2)", 1.0)
185 TEST_RESULT(Max1, "max(3,1,2)", 3.0)
186 TEST_RESULT(Min2, "min(1,2,3)", 1.0)
187 TEST_RESULT(Max2, "max(1,2,3)", 3.0)
188 TEST_RESULT(Min3, "min(2,3,1)", 1.0)
189 TEST_RESULT(Max3, "max(2,3,1)", 3.0)
190 
191 TEST_CONST(UnaryPlus, "+1", 1.0)
192 
193 TEST_CONST(UnaryMinus, "-1", -1.0)
194 TEST_EVAL(UnaryMinus, "-x", 1.0, -1.0)
195 
196 TEST_CONST(BinaryPlus, "1+2", 3.0)
197 TEST_EVAL(BinaryPlus, "x+2", 1, 3.0)
198 
199 TEST_CONST(BinaryMinus, "1-2", -1.0)
200 TEST_EVAL(BinaryMinus, "1-x", 2, -1.0)
201 
202 TEST_CONST(BinaryMul, "2*3", 6.0)
203 TEST_EVAL(BinaryMul, "x*3", 2, 6.0)
204 
205 TEST_CONST(BinaryDiv, "3/2", 1.5)
206 TEST_EVAL(BinaryDiv, "3/x", 2, 1.5)
207 
208 TEST_CONST(Arith1, "1 + -2 * 3", -5.0)
209 TEST_CONST(Arith2, "(1 + -2) * 3", -3.0)
210 TEST_CONST(Arith3, "-1 + 2 * 3", 5.0)
211 TEST_CONST(Arith4, "3 * (-2 + 1)", -3.0)
212 
213 TEST_EVAL(Arith1, "1 + -x * 3", 2, -5.0)
214 
215 TEST_CONST(Eq1, "1 == 1.0", TRUE_VAL)
216 TEST_CONST(Eq2, "1 == 2.0", FALSE_VAL)
217 TEST_CONST(Eq3, "True == 1", TRUE_VAL)
218 TEST_CONST(Eq4, "False == 0", TRUE_VAL)
219 
220 TEST_EVAL(Eq1, "1 == x", 1.0, TRUE_VAL)
221 TEST_EVAL(Eq2, "1 == x", 2.0, FALSE_VAL)
222 
223 TEST_CONST(NEq1, "1 != 1.0", FALSE_VAL)
224 TEST_CONST(NEq2, "1 != 2.0", TRUE_VAL)
225 
226 TEST_EVAL(NEq1, "1 != x", 1.0, FALSE_VAL)
227 TEST_EVAL(NEq2, "1 != x", 2.0, TRUE_VAL)
228 
229 TEST_CONST(Lt1, "1 < 1", FALSE_VAL)
230 TEST_CONST(Lt2, "1 < 2", TRUE_VAL)
231 TEST_CONST(Lt3, "2 < 1", FALSE_VAL)
232 
233 TEST_CONST(Le1, "1 <= 1", TRUE_VAL)
234 TEST_CONST(Le2, "1 <= 2", TRUE_VAL)
235 TEST_CONST(Le3, "2 <= 1", FALSE_VAL)
236 
237 TEST_CONST(Gt1, "1 > 1", FALSE_VAL)
238 TEST_CONST(Gt2, "1 > 2", FALSE_VAL)
239 TEST_CONST(Gt3, "2 > 1", TRUE_VAL)
240 
241 TEST_CONST(Ge1, "1 >= 1", TRUE_VAL)
242 TEST_CONST(Ge2, "1 >= 2", FALSE_VAL)
243 TEST_CONST(Ge3, "2 >= 1", TRUE_VAL)
244 
245 TEST_CONST(Cmp1, "3 == 1 + 2", TRUE_VAL)
246 
247 TEST_EVAL(Cmp1, "3 == x + 2", 1, TRUE_VAL)
248 TEST_EVAL(Cmp1b, "3 == x + 2", 1.5, FALSE_VAL)
249 
250 TEST_RESULT(CmpChain1, "1 < 2 < 3", TRUE_VAL)
251 TEST_RESULT(CmpChain2, "1 < 2 == 2", TRUE_VAL)
252 TEST_RESULT(CmpChain3, "1 < 2 > -1", TRUE_VAL)
253 TEST_RESULT(CmpChain4, "1 < 2 < 2 < 3", FALSE_VAL)
254 TEST_RESULT(CmpChain5, "1 < 2 <= 2 < 3", TRUE_VAL)
255 
256 TEST_EVAL(CmpChain1a, "1 < x < 3", 2, TRUE_VAL)
257 TEST_EVAL(CmpChain1b, "1 < x < 3", 1, FALSE_VAL)
258 TEST_EVAL(CmpChain1c, "1 < x < 3", 3, FALSE_VAL)
259 
260 TEST_CONST(Not1, "not 2", FALSE_VAL)
261 TEST_CONST(Not2, "not 0", TRUE_VAL)
262 TEST_CONST(Not3, "not not 2", TRUE_VAL)
263 
264 TEST_EVAL(Not1, "not x", 2, FALSE_VAL)
265 TEST_EVAL(Not2, "not x", 0, TRUE_VAL)
266 
267 TEST_RESULT(And1, "2 and 3", 3.0)
268 TEST_RESULT(And2, "0 and 3", 0.0)
269 
270 TEST_RESULT(Or1, "2 or 3", 2.0)
271 TEST_RESULT(Or2, "0 or 3", 3.0)
272 
273 TEST_RESULT(Bool1, "2 or 3 and 4", 2.0)
274 TEST_RESULT(Bool2, "not 2 or 3 and 4", 4.0)
275 
276 TEST(expr_pylike, Eval_Ternary1)
277 {
278  ExprPyLike_Parsed *expr = parse_for_eval("x / 2 if x < 4 else x - 2 if x < 8 else x*2 - 12",
279  true);
280 
281  for (int i = 0; i <= 10; i++) {
282  double x = i;
283  double v = (x < 4) ? (x / 2) : (x < 8) ? (x - 2) : (x * 2 - 12);
284 
285  verify_eval_result(expr, x, v);
286  }
287 
288  BLI_expr_pylike_free(expr);
289 }
290 
291 TEST(expr_pylike, MultipleArgs)
292 {
293  const char *names[3] = {"x", "y", "x"};
294  double values[3] = {1.0, 2.0, 3.0};
295 
297 
298  EXPECT_TRUE(BLI_expr_pylike_is_valid(expr));
299 
300  double result;
301  eExprPyLike_EvalStatus status = BLI_expr_pylike_eval(expr, values, 3, &result);
302 
304  EXPECT_EQ(result, 32.0);
305 
306  BLI_expr_pylike_free(expr);
307 }
308 
309 TEST(expr_pylike, UsingParam)
310 {
311  const char *names[3] = {"x", "y", "z"};
312 
314 
315  EXPECT_TRUE(BLI_expr_pylike_is_using_param(expr, 0));
316  EXPECT_FALSE(BLI_expr_pylike_is_using_param(expr, 1));
317  EXPECT_TRUE(BLI_expr_pylike_is_using_param(expr, 2));
318 
319  BLI_expr_pylike_free(expr);
320 }
321 
322 #define TEST_ERROR(name, str, x, code) \
323  TEST(expr_pylike, Error_##name) \
324  { \
325  expr_pylike_error_test(str, x, code); \
326  }
327 
328 TEST_ERROR(DivZero1, "0 / 0", 0.0, EXPR_PYLIKE_MATH_ERROR)
329 TEST_ERROR(DivZero2, "1 / 0", 0.0, EXPR_PYLIKE_DIV_BY_ZERO)
330 TEST_ERROR(DivZero3, "1 / x", 0.0, EXPR_PYLIKE_DIV_BY_ZERO)
331 TEST_ERROR(DivZero4, "1 / x", 1.0, EXPR_PYLIKE_SUCCESS)
332 
333 TEST_ERROR(SqrtDomain1, "sqrt(-1)", 0.0, EXPR_PYLIKE_MATH_ERROR)
334 TEST_ERROR(SqrtDomain2, "sqrt(x)", -1.0, EXPR_PYLIKE_MATH_ERROR)
335 TEST_ERROR(SqrtDomain3, "sqrt(x)", 0.0, EXPR_PYLIKE_SUCCESS)
336 
337 TEST_ERROR(PowDomain1, "pow(-1, 0.5)", 0.0, EXPR_PYLIKE_MATH_ERROR)
338 TEST_ERROR(PowDomain2, "pow(-1, x)", 0.5, EXPR_PYLIKE_MATH_ERROR)
339 TEST_ERROR(PowDomain3, "pow(-1, x)", 2.0, EXPR_PYLIKE_SUCCESS)
340 
341 TEST_ERROR(Mixed1, "sqrt(x) + 1 / max(0, x)", -1.0, EXPR_PYLIKE_MATH_ERROR)
342 TEST_ERROR(Mixed2, "sqrt(x) + 1 / max(0, x)", 0.0, EXPR_PYLIKE_DIV_BY_ZERO)
343 TEST_ERROR(Mixed3, "sqrt(x) + 1 / max(0, x)", 1.0, EXPR_PYLIKE_SUCCESS)
344 
345 TEST(expr_pylike, Error_Invalid)
346 {
347  ExprPyLike_Parsed *expr = BLI_expr_pylike_parse("", nullptr, 0);
348  double result;
349 
351 
353 }
354 
355 TEST(expr_pylike, Error_ArgumentCount)
356 {
357  ExprPyLike_Parsed *expr = parse_for_eval("x", false);
358  double result;
359 
361 
362  BLI_expr_pylike_free(expr);
363 }
eExprPyLike_EvalStatus
@ EXPR_PYLIKE_FATAL_ERROR
@ EXPR_PYLIKE_SUCCESS
@ EXPR_PYLIKE_DIV_BY_ZERO
@ EXPR_PYLIKE_MATH_ERROR
@ EXPR_PYLIKE_INVALID
eExprPyLike_EvalStatus BLI_expr_pylike_eval(struct ExprPyLike_Parsed *expr, const double *param_values, int param_values_len, double *r_result)
ExprPyLike_Parsed * BLI_expr_pylike_parse(const char *expression, const char **param_names, int param_names_len)
bool BLI_expr_pylike_is_using_param(struct ExprPyLike_Parsed *expr, int index)
bool BLI_expr_pylike_is_valid(struct ExprPyLike_Parsed *expr)
bool BLI_expr_pylike_is_constant(struct ExprPyLike_Parsed *expr)
#define TEST_RESULT(name, str, value)
EXPR_PYLIKE_DIV_BY_ZERO Error_Invalid
sqrt(x)+1/max(0
#define TEST_EVAL(name, str, x, value)
#define TEST_CONST(name, str, value)
#define TEST_PARSE_FAIL(name, str)
static ExprPyLike_Parsed * parse_for_eval(const char *str, bool nonconst)
static void verify_eval_result(ExprPyLike_Parsed *expr, double x, double value)
static void expr_pylike_const_test(const char *str, double value, bool force_const)
#define TRUE_VAL
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
static void expr_pylike_eval_test(const char *str, double x, double value)
static void expr_pylike_error_test(const char *str, double x, eExprPyLike_EvalStatus error)
#define FALSE_VAL
static void expr_pylike_parse_fail_test(const char *str)
BLI_expr_pylike_free(expr)
TEST(expr_pylike, Eval_Ternary1)
#define TEST_ERROR(name, str, x, code)
#define M_PI
Definition: BLI_math_base.h:20
#define ARRAY_SIZE(arr)
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
#define str(s)
static char ** names
Definition: makesdna.c:65
MINLINE float smoothstep(float edge0, float edge1, float x)
ccl_device_inline float3 pow(float3 v, float e)
Definition: math_float3.h:533
ccl_device_inline float3 log(float3 v)
Definition: math_float3.h:397
static void error(const char *str)
Definition: meshlaplacian.c:51
static float lerp(float t, float a, float b)
T clamp(const T &a, const T &min, const T &max)
#define min(a, b)
Definition: sort.c:35
float max