Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/tests/core/math/test_expression.h
10278 views
1
/**************************************************************************/
2
/* test_expression.h */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#pragma once
32
33
#include "core/math/expression.h"
34
35
#include "tests/test_macros.h"
36
37
namespace TestExpression {
38
39
TEST_CASE("[Expression] Integer arithmetic") {
40
Expression expression;
41
42
CHECK_MESSAGE(
43
expression.parse("-123456") == OK,
44
"Integer identity should parse successfully.");
45
CHECK_MESSAGE(
46
int(expression.execute()) == -123456,
47
"Integer identity should return the expected result.");
48
49
CHECK_MESSAGE(
50
expression.parse("2 + 3") == OK,
51
"Integer addition should parse successfully.");
52
CHECK_MESSAGE(
53
int(expression.execute()) == 5,
54
"Integer addition should return the expected result.");
55
56
CHECK_MESSAGE(
57
expression.parse("999999999999 + 999999999999") == OK,
58
"Large integer addition should parse successfully.");
59
CHECK_MESSAGE(
60
int64_t(expression.execute()) == 1'999'999'999'998,
61
"Large integer addition should return the expected result.");
62
63
CHECK_MESSAGE(
64
expression.parse("25 / 10") == OK,
65
"Integer / integer division should parse successfully.");
66
CHECK_MESSAGE(
67
int(expression.execute()) == 2,
68
"Integer / integer division should return the expected result.");
69
70
CHECK_MESSAGE(
71
expression.parse("2 * (6 + 14) / 2 - 5") == OK,
72
"Integer multiplication-addition-subtraction-division should parse successfully.");
73
CHECK_MESSAGE(
74
int(expression.execute()) == 15,
75
"Integer multiplication-addition-subtraction-division should return the expected result.");
76
}
77
78
TEST_CASE("[Expression] Floating-point arithmetic") {
79
Expression expression;
80
81
CHECK_MESSAGE(
82
expression.parse("-123.456") == OK,
83
"Float identity should parse successfully.");
84
CHECK_MESSAGE(
85
double(expression.execute()) == doctest::Approx(-123.456),
86
"Float identity should return the expected result.");
87
88
CHECK_MESSAGE(
89
expression.parse("2.0 + 3.0") == OK,
90
"Float addition should parse successfully.");
91
CHECK_MESSAGE(
92
double(expression.execute()) == doctest::Approx(5),
93
"Float addition should return the expected result.");
94
95
CHECK_MESSAGE(
96
expression.parse("3.0 / 10") == OK,
97
"Float / integer division should parse successfully.");
98
CHECK_MESSAGE(
99
double(expression.execute()) == doctest::Approx(0.3),
100
"Float / integer division should return the expected result.");
101
102
CHECK_MESSAGE(
103
expression.parse("3 / 10.0") == OK,
104
"Basic integer / float division should parse successfully.");
105
CHECK_MESSAGE(
106
double(expression.execute()) == doctest::Approx(0.3),
107
"Basic integer / float division should return the expected result.");
108
109
CHECK_MESSAGE(
110
expression.parse("3.0 / 10.0") == OK,
111
"Float / float division should parse successfully.");
112
CHECK_MESSAGE(
113
double(expression.execute()) == doctest::Approx(0.3),
114
"Float / float division should return the expected result.");
115
116
CHECK_MESSAGE(
117
expression.parse("2.5 * (6.0 + 14.25) / 2.0 - 5.12345") == OK,
118
"Float multiplication-addition-subtraction-division should parse successfully.");
119
CHECK_MESSAGE(
120
double(expression.execute()) == doctest::Approx(20.18905),
121
"Float multiplication-addition-subtraction-division should return the expected result.");
122
}
123
124
TEST_CASE("[Expression] Floating-point notation") {
125
Expression expression;
126
127
CHECK_MESSAGE(
128
expression.parse("2.") == OK,
129
"The expression should parse successfully.");
130
CHECK_MESSAGE(
131
double(expression.execute()) == doctest::Approx(2.0),
132
"The expression should return the expected result.");
133
134
CHECK_MESSAGE(
135
expression.parse("(2.)") == OK,
136
"The expression should parse successfully.");
137
CHECK_MESSAGE(
138
double(expression.execute()) == doctest::Approx(2.0),
139
"The expression should return the expected result.");
140
141
CHECK_MESSAGE(
142
expression.parse(".3") == OK,
143
"The expression should parse successfully.");
144
CHECK_MESSAGE(
145
double(expression.execute()) == doctest::Approx(0.3),
146
"The expression should return the expected result.");
147
148
CHECK_MESSAGE(
149
expression.parse("2.+5.") == OK,
150
"The expression should parse successfully.");
151
CHECK_MESSAGE(
152
double(expression.execute()) == doctest::Approx(7.0),
153
"The expression should return the expected result.");
154
155
CHECK_MESSAGE(
156
expression.parse(".3-.8") == OK,
157
"The expression should parse successfully.");
158
CHECK_MESSAGE(
159
double(expression.execute()) == doctest::Approx(-0.5),
160
"The expression should return the expected result.");
161
162
CHECK_MESSAGE(
163
expression.parse("2.+.2") == OK,
164
"The expression should parse successfully.");
165
CHECK_MESSAGE(
166
double(expression.execute()) == doctest::Approx(2.2),
167
"The expression should return the expected result.");
168
169
CHECK_MESSAGE(
170
expression.parse(".0*0.") == OK,
171
"The expression should parse successfully.");
172
CHECK_MESSAGE(
173
double(expression.execute()) == doctest::Approx(0.0),
174
"The expression should return the expected result.");
175
}
176
177
TEST_CASE("[Expression] Scientific notation") {
178
Expression expression;
179
180
CHECK_MESSAGE(
181
expression.parse("2.e5") == OK,
182
"The expression should parse successfully.");
183
CHECK_MESSAGE(
184
expression.parse("2.E5") == OK,
185
"The expression should parse successfully.");
186
CHECK_MESSAGE(
187
double(expression.execute()) == doctest::Approx(200'000),
188
"The expression should return the expected result.");
189
190
// The middle "e" is ignored here.
191
CHECK_MESSAGE(
192
expression.parse("2e5") == OK,
193
"The expression should parse successfully.");
194
CHECK_MESSAGE(
195
double(expression.execute()) == doctest::Approx(2e5),
196
"The expression should return the expected result.");
197
198
CHECK_MESSAGE(
199
expression.parse("2e.5") == OK,
200
"The expression should parse successfully.");
201
CHECK_MESSAGE(
202
double(expression.execute()) == doctest::Approx(2),
203
"The expression should return the expected result.");
204
}
205
206
TEST_CASE("[Expression] Underscored numeric literals") {
207
Expression expression;
208
209
CHECK_MESSAGE(
210
expression.parse("1_000_000") == OK,
211
"The expression should parse successfully.");
212
CHECK_MESSAGE(
213
expression.parse("1_000.000") == OK,
214
"The expression should parse successfully.");
215
CHECK_MESSAGE(
216
expression.parse("0xff_99_00") == OK,
217
"The expression should parse successfully.");
218
CHECK_MESSAGE(
219
expression.parse("0Xff_99_00") == OK,
220
"The expression should parse successfully.");
221
CHECK_MESSAGE(
222
expression.parse("0b10_11_00") == OK,
223
"The expression should parse successfully.");
224
CHECK_MESSAGE(
225
expression.parse("0B10_11_00") == OK,
226
"The expression should parse successfully.");
227
}
228
229
TEST_CASE("[Expression] Built-in functions") {
230
Expression expression;
231
232
CHECK_MESSAGE(
233
expression.parse("sqrt(pow(3, 2) + pow(4, 2))") == OK,
234
"The expression should parse successfully.");
235
CHECK_MESSAGE(
236
int(expression.execute()) == 5,
237
"`sqrt(pow(3, 2) + pow(4, 2))` should return the expected result.");
238
239
CHECK_MESSAGE(
240
expression.parse("snapped(sin(0.5), 0.01)") == OK,
241
"The expression should parse successfully.");
242
CHECK_MESSAGE(
243
double(expression.execute()) == doctest::Approx(0.48),
244
"`snapped(sin(0.5), 0.01)` should return the expected result.");
245
246
CHECK_MESSAGE(
247
expression.parse("pow(2.0, -2500)") == OK,
248
"The expression should parse successfully.");
249
CHECK_MESSAGE(
250
Math::is_zero_approx(double(expression.execute())),
251
"`pow(2.0, -2500)` should return the expected result (asymptotically zero).");
252
}
253
254
TEST_CASE("[Expression] Boolean expressions") {
255
Expression expression;
256
257
CHECK_MESSAGE(
258
expression.parse("24 >= 12") == OK,
259
"The boolean expression should parse successfully.");
260
CHECK_MESSAGE(
261
bool(expression.execute()),
262
"The boolean expression should evaluate to `true`.");
263
264
CHECK_MESSAGE(
265
expression.parse("1.0 < 1.25 && 1.25 < 2.0") == OK,
266
"The boolean expression should parse successfully.");
267
CHECK_MESSAGE(
268
bool(expression.execute()),
269
"The boolean expression should evaluate to `true`.");
270
271
CHECK_MESSAGE(
272
expression.parse("!2") == OK,
273
"The boolean expression should parse successfully.");
274
CHECK_MESSAGE(
275
!bool(expression.execute()),
276
"The boolean expression should evaluate to `false`.");
277
278
CHECK_MESSAGE(
279
expression.parse("!!2") == OK,
280
"The boolean expression should parse successfully.");
281
CHECK_MESSAGE(
282
bool(expression.execute()),
283
"The boolean expression should evaluate to `true`.");
284
285
CHECK_MESSAGE(
286
expression.parse("!0") == OK,
287
"The boolean expression should parse successfully.");
288
CHECK_MESSAGE(
289
bool(expression.execute()),
290
"The boolean expression should evaluate to `true`.");
291
292
CHECK_MESSAGE(
293
expression.parse("!!0") == OK,
294
"The boolean expression should parse successfully.");
295
CHECK_MESSAGE(
296
!bool(expression.execute()),
297
"The boolean expression should evaluate to `false`.");
298
299
CHECK_MESSAGE(
300
expression.parse("2 && 5") == OK,
301
"The boolean expression should parse successfully.");
302
CHECK_MESSAGE(
303
bool(expression.execute()),
304
"The boolean expression should evaluate to `true`.");
305
306
CHECK_MESSAGE(
307
expression.parse("0 || 0") == OK,
308
"The boolean expression should parse successfully.");
309
CHECK_MESSAGE(
310
!bool(expression.execute()),
311
"The boolean expression should evaluate to `false`.");
312
313
CHECK_MESSAGE(
314
expression.parse("(2 <= 4) && (2 > 5)") == OK,
315
"The boolean expression should parse successfully.");
316
CHECK_MESSAGE(
317
!bool(expression.execute()),
318
"The boolean expression should evaluate to `false`.");
319
}
320
321
TEST_CASE("[Expression] Expressions with variables") {
322
Expression expression;
323
324
PackedStringArray parameter_names = { "foo", "bar" };
325
CHECK_MESSAGE(
326
expression.parse("foo + bar + 50", parameter_names) == OK,
327
"The expression should parse successfully.");
328
Array values = { 60, 20 };
329
CHECK_MESSAGE(
330
int(expression.execute(values)) == 130,
331
"The expression should return the expected value.");
332
333
PackedStringArray parameter_names_invalid;
334
parameter_names_invalid.push_back("foo");
335
parameter_names_invalid.push_back("baz"); // Invalid parameter name.
336
CHECK_MESSAGE(
337
expression.parse("foo + bar + 50", parameter_names_invalid) == OK,
338
"The expression should parse successfully.");
339
Array values_invalid = { 60, 20 };
340
// Invalid parameters will parse successfully but print an error message when executing.
341
ERR_PRINT_OFF;
342
CHECK_MESSAGE(
343
int(expression.execute(values_invalid)) == 0,
344
"The expression should return the expected value.");
345
ERR_PRINT_ON;
346
347
// Mismatched argument count (more values than parameters).
348
PackedStringArray parameter_names_mismatch = { "foo", "bar" };
349
CHECK_MESSAGE(
350
expression.parse("foo + bar + 50", parameter_names_mismatch) == OK,
351
"The expression should parse successfully.");
352
Array values_mismatch = { 60, 20, 110 };
353
CHECK_MESSAGE(
354
int(expression.execute(values_mismatch)) == 130,
355
"The expression should return the expected value.");
356
357
// Mismatched argument count (more parameters than values).
358
PackedStringArray parameter_names_mismatch2 = { "foo", "bar", "baz" };
359
CHECK_MESSAGE(
360
expression.parse("foo + bar + baz + 50", parameter_names_mismatch2) == OK,
361
"The expression should parse successfully.");
362
Array values_mismatch2 = { 60, 20 };
363
// Having more parameters than values will parse successfully but print an
364
// error message when executing.
365
ERR_PRINT_OFF;
366
CHECK_MESSAGE(
367
int(expression.execute(values_mismatch2)) == 0,
368
"The expression should return the expected value.");
369
ERR_PRINT_ON;
370
}
371
372
TEST_CASE("[Expression] Invalid expressions") {
373
Expression expression;
374
375
CHECK_MESSAGE(
376
expression.parse("\\") == ERR_INVALID_PARAMETER,
377
"The expression shouldn't parse successfully.");
378
379
CHECK_MESSAGE(
380
expression.parse("0++") == ERR_INVALID_PARAMETER,
381
"The expression shouldn't parse successfully.");
382
383
CHECK_MESSAGE(
384
expression.parse("()") == ERR_INVALID_PARAMETER,
385
"The expression shouldn't parse successfully.");
386
387
CHECK_MESSAGE(
388
expression.parse("()()") == ERR_INVALID_PARAMETER,
389
"The expression shouldn't parse successfully.");
390
391
CHECK_MESSAGE(
392
expression.parse("() - ()") == ERR_INVALID_PARAMETER,
393
"The expression shouldn't parse successfully.");
394
395
CHECK_MESSAGE(
396
expression.parse("() * 12345") == ERR_INVALID_PARAMETER,
397
"The expression shouldn't parse successfully.");
398
399
CHECK_MESSAGE(
400
expression.parse("() * 12345") == ERR_INVALID_PARAMETER,
401
"The expression shouldn't parse successfully.");
402
403
CHECK_MESSAGE(
404
expression.parse("123'456") == ERR_INVALID_PARAMETER,
405
"The expression shouldn't parse successfully.");
406
407
CHECK_MESSAGE(
408
expression.parse("123\"456") == ERR_INVALID_PARAMETER,
409
"The expression shouldn't parse successfully.");
410
}
411
412
TEST_CASE("[Expression] Unusual expressions") {
413
Expression expression;
414
415
// Redundant parentheses don't cause a parse error as long as they're matched.
416
CHECK_MESSAGE(
417
expression.parse("(((((((((((((((666)))))))))))))))") == OK,
418
"The expression should parse successfully.");
419
420
// Using invalid identifiers doesn't cause a parse error.
421
ERR_PRINT_OFF;
422
CHECK_MESSAGE(
423
expression.parse("hello + hello") == OK,
424
"The expression should parse successfully.");
425
CHECK_MESSAGE(
426
int(expression.execute()) == 0,
427
"The expression should return the expected result.");
428
ERR_PRINT_ON;
429
430
ERR_PRINT_OFF;
431
CHECK_MESSAGE(
432
expression.parse("$1.00 + ???5") == OK,
433
"The expression should parse successfully.");
434
CHECK_MESSAGE(
435
int(expression.execute()) == 0,
436
"The expression should return the expected result.");
437
ERR_PRINT_ON;
438
439
// Commas can't be used as a decimal parameter.
440
CHECK_MESSAGE(
441
expression.parse("123,456") == OK,
442
"The expression should parse successfully.");
443
CHECK_MESSAGE(
444
int(expression.execute()) == 123,
445
"The expression should return the expected result.");
446
447
// Spaces can't be used as a separator for large numbers.
448
CHECK_MESSAGE(
449
expression.parse("123 456") == OK,
450
"The expression should parse successfully.");
451
CHECK_MESSAGE(
452
int(expression.execute()) == 123,
453
"The expression should return the expected result.");
454
455
// Division by zero is accepted, even though it prints an error message normally.
456
CHECK_MESSAGE(
457
expression.parse("-25.4 / 0") == OK,
458
"The expression should parse successfully.");
459
ERR_PRINT_OFF;
460
CHECK_MESSAGE(
461
Math::is_inf(double(expression.execute())),
462
"`-25.4 / 0` should return inf.");
463
ERR_PRINT_ON;
464
465
CHECK_MESSAGE(
466
expression.parse("0 / 0") == OK,
467
"The expression should parse successfully.");
468
ERR_PRINT_OFF;
469
CHECK_MESSAGE(
470
int(expression.execute()) == 0,
471
"`0 / 0` should return 0.");
472
ERR_PRINT_ON;
473
474
// The tests below currently crash the engine.
475
//
476
//CHECK_MESSAGE(
477
// expression.parse("(-9223372036854775807 - 1) % -1") == OK,
478
// "The expression should parse successfully.");
479
//CHECK_MESSAGE(
480
// int64_t(expression.execute()) == 0,
481
// "`(-9223372036854775807 - 1) % -1` should return the expected result.");
482
//
483
//CHECK_MESSAGE(
484
// expression.parse("(-9223372036854775807 - 1) / -1") == OK,
485
// "The expression should parse successfully.");
486
//CHECK_MESSAGE(
487
// int64_t(expression.execute()) == 0,
488
// "`(-9223372036854775807 - 1) / -1` should return the expected result.");
489
}
490
} // namespace TestExpression
491
492