master
c 1,213 lines 52.7 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "libnetdata/libnetdata.h"
4 #include "eval-internal.h"
5
6 // Mock variable lookup function for testing
7 static bool test_variable_lookup(STRING *variable, void *data __maybe_unused, NETDATA_DOUBLE *result) {
8 const char *var_name = string2str(variable);
9
10 // Basic variables
11 if (strcmp(var_name, "var1") == 0) {
12 *result = 42.0;
13 return true;
14 }
15 else if (strcmp(var_name, "var2") == 0) {
16 *result = 24.0;
17 return true;
18 }
19 else if (strcmp(var_name, "zero") == 0) {
20 *result = 0.0;
21 return true;
22 }
23 else if (strcmp(var_name, "negative") == 0) {
24 *result = -10.0;
25 return true;
26 }
27
28 // Special values
29 else if (strcmp(var_name, "nan_var") == 0) {
30 *result = NAN;
31 return true;
32 }
33 else if (strcmp(var_name, "inf_var") == 0) {
34 *result = INFINITY;
35 return true;
36 }
37
38 // Variables with spaces (for braced variables)
39 else if (strcmp(var_name, "this variable") == 0) {
40 *result = 100.0;
41 return true;
42 }
43 else if (strcmp(var_name, "this") == 0) {
44 *result = 50.0;
45 return true;
46 }
47
48 // Variables that start with numbers
49 else if (strcmp(var_name, "1var") == 0) {
50 *result = 42.0; // Using the same value as var1 for consistency
51 return true;
52 }
53 else if (strcmp(var_name, "_var") == 0) {
54 *result = 76.0;
55 return true;
56 }
57 else if (strcmp(var_name, "1.var") == 0) {
58 *result = 77.0;
59 return true;
60 }
61
62 // Variables with dots
63 else if (strcmp(var_name, "var.1") == 0) {
64 *result = 78.0;
65 return true;
66 }
67
68 // Variables with hyphens
69 else if (strcmp(var_name, "var-1") == 0) {
70 *result = 79.0;
71 return true;
72 }
73 else if (strcmp(var_name, "var-with-hyphens") == 0) {
74 *result = 100.0;
75 return true;
76 }
77
78 // Indexed variables with spaces (Core X)
79 else if (strcmp(var_name, "Core 0") == 0) {
80 *result = 25.0;
81 return true;
82 }
83 else if (strcmp(var_name, "Core 1") == 0) {
84 *result = 35.0;
85 return true;
86 }
87 else if (strcmp(var_name, "Core 2") == 0) {
88 *result = 15.0;
89 return true;
90 }
91 else if (strcmp(var_name, "Core 3") == 0) {
92 *result = 40.0;
93 return true;
94 }
95 else if (strcmp(var_name, "Core 02") == 0) {
96 *result = 15.0; // Same as Core 2 for testing
97 return true;
98 }
99
100 // Alternative CPU core notation
101 else if (strcmp(var_name, "Core1") == 0) {
102 *result = 35.0; // Same as Core 1
103 return true;
104 }
105 else if (strcmp(var_name, "Core2") == 0) {
106 *result = 15.0; // Same as Core 2
107 return true;
108 }
109 else if (strcmp(var_name, "Core3") == 0) {
110 *result = 40.0; // Same as Core 3
111 return true;
112 }
113
114 // Time-related variables
115 else if (strcmp(var_name, "last_collected_t") == 0) {
116 *result = 1713400000.0; // Example timestamp
117 return true;
118 }
119 else if (strcmp(var_name, "now") == 0) {
120 *result = 1713400030.0; // 30 seconds after last_collected_t
121 return true;
122 }
123 else if (strcmp(var_name, "last_scrub") == 0) {
124 *result = 3600.0; // 1 hour in seconds
125 return true;
126 }
127
128 // Special variables with numeric modifiers
129 else if (strcmp(var_name, "1hour_packet_drops_inbound") == 0) {
130 *result = 250.0;
131 return true;
132 }
133 else if (strcmp(var_name, "1hour_packet_drops_outbound") == 0) {
134 *result = 150.0;
135 return true;
136 }
137 else if (strcmp(var_name, "1m_ipv4_udp_receive_buffer_errors") == 0) {
138 *result = 5000.0;
139 return true;
140 }
141 else if (strcmp(var_name, "active_processors") == 0) {
142 *result = 8.0;
143 return true;
144 }
145
146 // Bandwidth related
147 else if (strcmp(var_name, "bandwidth_1m_avg_of_now") == 0) {
148 *result = 1050.0;
149 return true;
150 }
151 else if (strcmp(var_name, "bandwidth_1m_avg_of_previous_1m") == 0) {
152 *result = 1000.0;
153 return true;
154 }
155 else if (strcmp(var_name, "bandwidth_1m_max_of_now") == 0) {
156 *result = 1500.0;
157 return true;
158 }
159 else if (strcmp(var_name, "bandwidth_1m_max_of_previous_1m") == 0) {
160 *result = 1400.0;
161 return true;
162 }
163
164 // Additional variables for memory tests
165 else if (strcmp(var_name, "mem") == 0) {
166 *result = 12000.0;
167 return true;
168 }
169 else if (strcmp(var_name, "tcp_mem_pressure") == 0) {
170 *result = 10000.0;
171 return true;
172 }
173 else if (strcmp(var_name, "tcp_mem_high") == 0) {
174 *result = 9000.0;
175 return true;
176 }
177 else if (strcmp(var_name, "pidmax") == 0) {
178 *result = 32768.0;
179 return true;
180 }
181 else if (strcmp(var_name, "arrays") == 0) {
182 *result = 128.0;
183 return true;
184 }
185 else if (strcmp(var_name, "ipc.semaphores.arrays.max") == 0) {
186 *result = 256.0;
187 return true;
188 }
189 else if (strcmp(var_name, "ipc_semaphores_arrays_max") == 0) {
190 *result = 256.0; // Same as above for testing alternative notation
191 return true;
192 }
193
194 // Labels syntax test
195 else if (strcmp(var_name, "label:host") == 0) {
196 *result = 1.0; // Non-zero value to simulate a non-match
197 return true;
198 }
199
200 // Color thresholds
201 else if (strcmp(var_name, "green") == 0) {
202 *result = 30.0;
203 return true;
204 }
205 else if (strcmp(var_name, "red") == 0) {
206 *result = 80.0;
207 return true;
208 }
209
210 // Hierarchical variable names for system metrics
211 else if (strcmp(var_name, "system.ram.free") == 0) {
212 *result = 1000.0;
213 return true;
214 }
215 else if (strcmp(var_name, "system.ram.used") == 0) {
216 *result = 2000.0;
217 return true;
218 }
219 else if (strcmp(var_name, "system.ram.cached") == 0) {
220 *result = 500.0;
221 return true;
222 }
223 else if (strcmp(var_name, "system.ram.buffers") == 0) {
224 *result = 300.0;
225 return true;
226 }
227 else if (strcmp(var_name, "system.ram.swap") == 0) {
228 *result = 1000.0;
229 return true;
230 }
231 else if (strcmp(var_name, "system.ram.active") == 0) {
232 *result = 1500.0;
233 return true;
234 }
235 else if (strcmp(var_name, "system.ram.inactive") == 0) {
236 *result = 400.0;
237 return true;
238 }
239 else if (strcmp(var_name, "system.ram.wired") == 0) {
240 *result = 500.0;
241 return true;
242 }
243 else if (strcmp(var_name, "system.ram.cache") == 0) {
244 *result = 800.0;
245 return true;
246 }
247 else if (strcmp(var_name, "system.ram.laundry") == 0) {
248 *result = 200.0;
249 return true;
250 }
251 else if (strcmp(var_name, "system.ram.used_ram_to_ignore") == 0) {
252 *result = 200.0;
253 return true;
254 }
255
256 // Variables for real-world test expressions
257 else if (strcmp(var_name, "avail") == 0) {
258 *result = 950.0;
259 return true;
260 }
261 else if (strcmp(var_name, "active") == 0) {
262 *result = 1500.0;
263 return true;
264 }
265 else if (strcmp(var_name, "wired") == 0) {
266 *result = 500.0;
267 return true;
268 }
269 else if (strcmp(var_name, "laundry") == 0) {
270 *result = 200.0;
271 return true;
272 }
273 else if (strcmp(var_name, "buffers") == 0) {
274 *result = 300.0;
275 return true;
276 }
277 else if (strcmp(var_name, "cache") == 0) {
278 *result = 800.0;
279 return true;
280 }
281 else if (strcmp(var_name, "free") == 0) {
282 *result = 1000.0;
283 return true;
284 }
285 else if (strcmp(var_name, "inactive") == 0) {
286 *result = 400.0;
287 return true;
288 }
289 else if (strcmp(var_name, "used_ram_to_ignore") == 0) {
290 *result = 200.0;
291 return true;
292 }
293
294 // From dataset examples with status variables
295 else if (strcmp(var_name, "status") == 0) {
296 *result = 1.0; // WARNING status
297 return true;
298 }
299 else if (strcmp(var_name, "CRITICAL") == 0) {
300 *result = 2.0;
301 return true;
302 }
303 else if (strcmp(var_name, "WARNING") == 0) {
304 *result = 1.0;
305 return true;
306 }
307 else if (strcmp(var_name, "10m_acquiring_requests") == 0) {
308 *result = 100.0;
309 return true;
310 }
311 else if (strcmp(var_name, "sent") == 0) {
312 *result = 1000.0;
313 return true;
314 }
315 else if (strcmp(var_name, "buffered") == 0) {
316 *result = 500.0;
317 return true;
318 }
319 else if (strcmp(var_name, "lost") == 0) {
320 *result = -10.0;
321 return true;
322 }
323 else if (strcmp(var_name, "offset") == 0) {
324 *result = -5.0;
325 return true;
326 }
327
328 return false; // Variable not found
329 }
330
331 typedef struct {
332 const char *expression;
333 NETDATA_DOUBLE expected_result;
334 int expected_error;
335 bool should_parse;
336 } TestCase;
337
338 typedef struct {
339 const char *name;
340 TestCase *test_cases;
341 int test_count;
342 } TestGroup;
343
344 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
345
346 static void run_test_group(TestGroup *group) {
347 printf("\n=== Running Test Group: %s ===\n", group->name);
348
349 int passed = 0;
350 int failed = 0;
351
352 for (int i = 0; i < group->test_count; i++) {
353 TestCase *tc = &group->test_cases[i];
354 const char *failed_at = NULL;
355 int error = 0;
356 bool test_failed = false;
357 char error_message[1024] = "";
358
359 printf("Test %d: %s\n", i + 1, tc->expression);
360
361 // Try to parse the expression
362 EVAL_EXPRESSION *exp = expression_parse(tc->expression, &failed_at, &error);
363
364 // Check if parsing succeeded as expected
365 if (tc->should_parse && !exp) {
366 snprintf(error_message, sizeof(error_message),
367 "Expected parsing to succeed, but it failed with error %d (%s)",
368 error, expression_strerror(error));
369 test_failed = true;
370 }
371 else if (!tc->should_parse && exp) {
372 snprintf(error_message, sizeof(error_message),
373 "Expected parsing to fail, but it succeeded");
374 test_failed = true;
375 }
376
377 // If the expression parsed successfully, evaluate it
378 if (exp) {
379 // Set up the variable lookup callback
380 expression_set_variable_lookup_callback(exp, test_variable_lookup, NULL);
381
382 // Evaluate the expression
383 int eval_result = expression_evaluate(exp);
384
385 // Check if there was an error during evaluation
386 if (tc->expected_error != EVAL_ERROR_OK && exp->error == EVAL_ERROR_OK) {
387 snprintf(error_message, sizeof(error_message),
388 "Expected evaluation error %d, but got no error",
389 tc->expected_error);
390 test_failed = true;
391 }
392 else if (tc->expected_error == EVAL_ERROR_OK && exp->error != EVAL_ERROR_OK) {
393 snprintf(error_message, sizeof(error_message),
394 "Expected no evaluation error, but got error %d (%s)",
395 exp->error, expression_strerror(exp->error));
396 test_failed = true;
397 }
398 else if (tc->expected_error != EVAL_ERROR_OK && exp->error != tc->expected_error) {
399 snprintf(error_message, sizeof(error_message),
400 "Expected evaluation error %d, but got error %d (%s)",
401 tc->expected_error, exp->error, expression_strerror(exp->error));
402 test_failed = true;
403 }
404
405 // Check the evaluation result
406 if (tc->expected_error == EVAL_ERROR_OK) {
407 if (isnan(tc->expected_result) && !isnan(exp->result)) {
408 snprintf(error_message, sizeof(error_message),
409 "Expected NaN result, but got %f", exp->result);
410 test_failed = true;
411 }
412 else if (isinf(tc->expected_result) && !isinf(exp->result)) {
413 snprintf(error_message, sizeof(error_message),
414 "Expected Inf result, but got %f", exp->result);
415 test_failed = true;
416 }
417 else if (!isnan(tc->expected_result) && !isinf(tc->expected_result) &&
418 !isnan(exp->result) && !isinf(exp->result) &&
419 fabs(tc->expected_result - exp->result) > 0.000001) {
420 snprintf(error_message, sizeof(error_message),
421 "Expected result %f, but got %f",
422 tc->expected_result, exp->result);
423 test_failed = true;
424 }
425 }
426
427 // Print additional information for debugging
428 printf(" Parsed as: %s\n", expression_parsed_as(exp));
429
430 if (eval_result) {
431 printf(" Evaluated to: %f\n", expression_result(exp));
432 } else {
433 printf(" Evaluation failed: %s\n", expression_error_msg(exp));
434 }
435
436 // Special handling for API function tests
437 if (strcmp(group->name, "API Function Tests") == 0) {
438 if (strstr(tc->expression, "hardcoded_var") != NULL) {
439 // Test the expression_hardcode_variable function
440 STRING *var = string_strdupz("hardcoded_var");
441 expression_hardcode_variable(exp, var, 123.456);
442 string_freez(var);
443
444 // Re-evaluate the expression
445 expression_evaluate(exp);
446
447 // Check if hardcoded variable works correctly
448 if (exp->error != EVAL_ERROR_OK || fabs(exp->result - 123.456) > 0.000001) {
449 snprintf(error_message, sizeof(error_message),
450 "expression_hardcode_variable failed: expected 123.456, got %f (error: %d)",
451 exp->result, exp->error);
452 test_failed = true;
453 } else {
454 printf(" expression_hardcode_variable test passed!\n");
455 }
456 }
457 else if (strcmp(tc->expression, "1 + 2") == 0) {
458 // Test expression_source
459 const char *source = expression_source(exp);
460 if (strcmp(source, "1 + 2") != 0) {
461 snprintf(error_message, sizeof(error_message),
462 "expression_source failed: expected '1 + 2', got '%s'",
463 source);
464 test_failed = true;
465 } else {
466 printf(" expression_source test passed!\n");
467 }
468
469 // Test expression_parsed_as
470 const char *parsed = expression_parsed_as(exp);
471 if (parsed == NULL || strlen(parsed) == 0) {
472 snprintf(error_message, sizeof(error_message),
473 "expression_parsed_as failed: got empty or NULL result");
474 test_failed = true;
475 } else {
476 printf(" expression_parsed_as test passed! Result: %s\n", parsed);
477 }
478
479 // Test expression_result
480 NETDATA_DOUBLE result = expression_result(exp);
481 if (fabs(result - 3.0) > 0.000001) {
482 snprintf(error_message, sizeof(error_message),
483 "expression_result failed: expected 3.0, got %f",
484 result);
485 test_failed = true;
486 } else {
487 printf(" expression_result test passed!\n");
488 }
489 }
490 else if (strcmp(tc->expression, "bad/syntax") == 0) {
491 // This case is for testing expression_error_msg, but it is already tested
492 // in the main evaluation loop when errors occur.
493 printf(" expression_error_msg is tested during evaluation failures\n");
494 }
495 }
496
497 // Clean up
498 expression_free(exp);
499 }
500 else if (!tc->should_parse) {
501 printf(" Parsing failed as expected at: %s\n",
502 failed_at ? ((*failed_at) ? failed_at : "<END OF EXPRESSION>") : "<NONE>");
503 }
504
505 // Report test result
506 if (test_failed) {
507 #ifdef USE_RE2C_LEMON_PARSER
508 printf(" [RE2C_LEMON] FAILED: %s\n", error_message);
509 #else
510 printf(" [RECURSIVE] FAILED: %s\n", error_message);
511 #endif
512 failed++;
513 } else {
514 printf(" PASSED\n");
515 passed++;
516 }
517 }
518
519 printf("\nGroup Results: %d tests, %d passed, %d failed\n",
520 passed + failed, passed, failed);
521 }
522
523 static TestCase arithmetic_tests[] = {
524 {"1 + 2", 3.0, EVAL_ERROR_OK, true},
525 {"5 - 3", 2.0, EVAL_ERROR_OK, true},
526 {"4 * 5", 20.0, EVAL_ERROR_OK, true},
527 {"10 / 2", 5.0, EVAL_ERROR_OK, true},
528 {"10 / 0", 0.0, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Netdata reports error for division by zero
529 {"-10", -10.0, EVAL_ERROR_OK, true},
530 {"+5", 5.0, EVAL_ERROR_OK, true},
531 {"5 + -3", 2.0, EVAL_ERROR_OK, true},
532 {"5 * -3", -15.0, EVAL_ERROR_OK, true},
533 {"1 + 2 * 3", 7.0, EVAL_ERROR_OK, true},
534 {"(1 + 2) * 3", 9.0, EVAL_ERROR_OK, true},
535 {"10.5 + 2.5", 13.0, EVAL_ERROR_OK, true},
536 {"10.5 * 2", 21.0, EVAL_ERROR_OK, true},
537 {"5.5 / 2", 2.75, EVAL_ERROR_OK, true},
538 {"1.5e2 + 2", 152.0, EVAL_ERROR_OK, true},
539 {"1+2*3+4", 11.0, EVAL_ERROR_OK, true},
540 };
541
542 // Test cases for comparison operations
543 static TestCase comparison_tests[] = {
544 {"1 == 1", 1.0, EVAL_ERROR_OK, true},
545 {"1 == 2", 0.0, EVAL_ERROR_OK, true},
546 {"1 != 2", 1.0, EVAL_ERROR_OK, true},
547 {"1 != 1", 0.0, EVAL_ERROR_OK, true},
548 {"5 > 3", 1.0, EVAL_ERROR_OK, true},
549 {"3 > 5", 0.0, EVAL_ERROR_OK, true},
550 {"3 < 5", 1.0, EVAL_ERROR_OK, true},
551 {"5 < 3", 0.0, EVAL_ERROR_OK, true},
552 {"5 >= 5", 1.0, EVAL_ERROR_OK, true},
553 {"5 >= 6", 0.0, EVAL_ERROR_OK, true},
554 {"5 <= 5", 1.0, EVAL_ERROR_OK, true},
555 {"5 <= 4", 0.0, EVAL_ERROR_OK, true},
556 {"3 > 2 > 1", 0.0, EVAL_ERROR_OK, true}, // This is (3 > 2) > 1, which is 1 > 1, which is false
557 };
558
559 // Test cases for logical operations
560 static TestCase logical_tests[] = {
561 {"1 && 1", 1.0, EVAL_ERROR_OK, true},
562 {"1 && 0", 0.0, EVAL_ERROR_OK, true},
563 {"0 && 1", 0.0, EVAL_ERROR_OK, true},
564 {"0 && 0", 0.0, EVAL_ERROR_OK, true},
565 {"1 || 1", 1.0, EVAL_ERROR_OK, true},
566 {"1 || 0", 1.0, EVAL_ERROR_OK, true},
567 {"0 || 1", 1.0, EVAL_ERROR_OK, true},
568 {"0 || 0", 0.0, EVAL_ERROR_OK, true},
569 {"!1", 0.0, EVAL_ERROR_OK, true},
570 {"!0", 1.0, EVAL_ERROR_OK, true},
571 {"!(1 && 0)", 1.0, EVAL_ERROR_OK, true},
572 {"1 && !0", 1.0, EVAL_ERROR_OK, true},
573 {"0 || !(1 && 0)", 1.0, EVAL_ERROR_OK, true},
574
575 // Tests for word operators (AND, OR)
576 {"1 AND 1", 1.0, EVAL_ERROR_OK, true},
577 {"1 AND 0", 0.0, EVAL_ERROR_OK, true},
578 {"0 AND 1", 0.0, EVAL_ERROR_OK, true},
579 {"0 AND 0", 0.0, EVAL_ERROR_OK, true},
580 {"1 OR 1", 1.0, EVAL_ERROR_OK, true},
581 {"1 OR 0", 1.0, EVAL_ERROR_OK, true},
582 {"0 OR 1", 1.0, EVAL_ERROR_OK, true},
583 {"0 OR 0", 0.0, EVAL_ERROR_OK, true},
584 {"NOT 1", 0.0, EVAL_ERROR_OK, true},
585 {"NOT 0", 1.0, EVAL_ERROR_OK, true},
586 {"NOT(1 AND 0)", 1.0, EVAL_ERROR_OK, true},
587 {"1 AND NOT 0", 1.0, EVAL_ERROR_OK, true},
588 {"0 OR NOT(1 AND 0)", 1.0, EVAL_ERROR_OK, true},
589 {"(1 AND 1) OR (0 AND 1)", 1.0, EVAL_ERROR_OK, true},
590
591 // Mixed symbol and word operators
592 {"1 AND (0 || 1)", 1.0, EVAL_ERROR_OK, true},
593 {"(1 && 0) OR 1", 1.0, EVAL_ERROR_OK, true},
594 {"NOT (1 && 0) OR (NOT 0 AND 1)", 1.0, EVAL_ERROR_OK, true},
595
596 // Case-insensitive logical operators
597 {"1 and 1", 1.0, EVAL_ERROR_OK, true},
598 {"0 or 1", 1.0, EVAL_ERROR_OK, true},
599 {"not 0", 1.0, EVAL_ERROR_OK, true},
600 {"1 And 0", 0.0, EVAL_ERROR_OK, true},
601 {"0 Or 1", 1.0, EVAL_ERROR_OK, true},
602 {"Not 1", 0.0, EVAL_ERROR_OK, true},
603 };
604
605 // Test cases for variable usage
606 static TestCase variable_tests[] = {
607 // Basic variable tests
608 {"$var1", 42.0, EVAL_ERROR_OK, true},
609 {"$var2", 24.0, EVAL_ERROR_OK, true},
610 {"$var1 + $var2", 66.0, EVAL_ERROR_OK, true},
611 {"$var1 * $var2", 1008.0, EVAL_ERROR_OK, true},
612 {"$var1 > $var2", 1.0, EVAL_ERROR_OK, true},
613 {"$var1 < $var2", 0.0, EVAL_ERROR_OK, true},
614 {"$var1 && $var2", 1.0, EVAL_ERROR_OK, true},
615 {"$zero && $var1", 0.0, EVAL_ERROR_OK, true},
616
617 // Variables with different notations
618 {"$var1", 42.0, EVAL_ERROR_OK, true}, // Test dollar sign prefix
619 {"${var1}", 42.0, EVAL_ERROR_OK, true}, // Test with curly braces
620 {"${this variable}", 100.0, EVAL_ERROR_OK, true}, // Variable with space
621 {"$unknown", 0.0, EVAL_ERROR_UNKNOWN_VARIABLE, true},
622
623 // Variables starting with numbers (from real-world usage)
624 {"$1var", 42.0, EVAL_ERROR_OK, true},
625 {"$1.var", 77.0, EVAL_ERROR_OK, true},
626 {"$var.1", 78.0, EVAL_ERROR_OK, true},
627
628 // Variables with special characters
629 {"$var-1", 79.0, EVAL_ERROR_UNKNOWN_VARIABLE, true},
630 {"${var-with-hyphens}", 100.0, EVAL_ERROR_OK, true},
631
632 // Hierarchical variable names with dots
633 {"$system.ram.free", 1000.0, EVAL_ERROR_OK, true},
634 {"$system.ram.used", 2000.0, EVAL_ERROR_OK, true},
635 {"$system.ram.cached", 500.0, EVAL_ERROR_OK, true},
636 {"$system.ram.buffers", 300.0, EVAL_ERROR_OK, true},
637
638 // Real-world examples from the dataset - variable with math expressions
639 {"$avail * 100 / ($system.ram.used + $system.ram.cached + $system.ram.free + $system.ram.buffers)", 25.0, EVAL_ERROR_OK, true},
640 };
641
642 // Test cases for function calls
643 static TestCase function_tests[] = {
644 {"abs(5)", 5.0, EVAL_ERROR_OK, true},
645 {"abs(-5)", 5.0, EVAL_ERROR_OK, true},
646 {"abs(0)", 0.0, EVAL_ERROR_OK, true},
647 {"abs($var1)", 42.0, EVAL_ERROR_OK, true},
648 {"abs($negative)", 10.0, EVAL_ERROR_OK, true},
649 {"abs(1 + -3)", 2.0, EVAL_ERROR_OK, true},
650 {"abs($var1 - $var2)", 18.0, EVAL_ERROR_OK, true},
651 {"abs(abs(-5))", 5.0, EVAL_ERROR_OK, true}, // Nested function call
652 };
653
654 // Test cases for special values
655 // In Netdata, NaN values cause VALUE_IS_UNSET errors and Infinity causes VALUE_IS_INFINITE errors
656 static TestCase special_value_tests[] = {
657 // NaN tests - Netdata rejects them with VALUE_IS_UNSET error
658 {"$nan_var", 0.0, EVAL_ERROR_VALUE_IS_NAN, true},
659
660 // Comparison operators with NaN - these should work as they just check NaN status
661 {"$nan_var == 5", 0.0, EVAL_ERROR_OK, true},
662 {"$nan_var != 5", 1.0, EVAL_ERROR_OK, true},
663 {"$nan_var > 5", 0.0, EVAL_ERROR_OK, true},
664 {"$nan_var < 5", 0.0, EVAL_ERROR_OK, true},
665 {"$nan_var >= 5", 0.0, EVAL_ERROR_OK, true},
666 {"$nan_var <= 5", 0.0, EVAL_ERROR_OK, true},
667
668 // NaN self-comparison (Netdata treats NaN == NaN as true, which is different from IEEE 754)
669 {"$nan_var == $nan_var", 1.0, EVAL_ERROR_OK, true},
670 {"$nan_var != $nan_var", 0.0, EVAL_ERROR_OK, true},
671 {"$nan_var > $nan_var", 0.0, EVAL_ERROR_OK, true},
672 {"$nan_var < $nan_var", 0.0, EVAL_ERROR_OK, true},
673 {"$nan_var >= $nan_var", 0.0, EVAL_ERROR_OK, true},
674 {"$nan_var <= $nan_var", 0.0, EVAL_ERROR_OK, true},
675
676 // Logical operations with NaN
677 {"$nan_var && 1", 0.0, EVAL_ERROR_OK, true},
678 {"$nan_var || 1", 1.0, EVAL_ERROR_OK, true},
679 {"$nan_var && 0", 0.0, EVAL_ERROR_OK, true},
680 {"$nan_var || 0", 0.0, EVAL_ERROR_OK, true},
681 {"!$nan_var", 1.0, EVAL_ERROR_OK, true},
682
683 // Ternary with NaN
684 {"($nan_var) ? 1 : 2", 2.0, EVAL_ERROR_OK, true},
685
686 // Infinity tests - Netdata rejects with VALUE_IS_INFINITE error
687 {"$inf_var", 0.0, EVAL_ERROR_VALUE_IS_INFINITE, true},
688
689 // Comparison operators with Infinity - these work
690 {"$inf_var == 5", 0.0, EVAL_ERROR_OK, true},
691 {"$inf_var != 5", 1.0, EVAL_ERROR_OK, true},
692 {"$inf_var > 5", 1.0, EVAL_ERROR_OK, true},
693 {"$inf_var < 5", 0.0, EVAL_ERROR_OK, true},
694 {"$inf_var >= 5", 1.0, EVAL_ERROR_OK, true},
695 {"$inf_var <= 5", 0.0, EVAL_ERROR_OK, true},
696
697 // Infinity self-comparison
698 {"$inf_var == $inf_var", 1.0, EVAL_ERROR_OK, true},
699 {"$inf_var != $inf_var", 0.0, EVAL_ERROR_OK, true},
700 {"$inf_var > $inf_var", 0.0, EVAL_ERROR_OK, true},
701 {"$inf_var < $inf_var", 0.0, EVAL_ERROR_OK, true},
702 {"$inf_var >= $inf_var", 1.0, EVAL_ERROR_OK, true},
703 {"$inf_var <= $inf_var", 1.0, EVAL_ERROR_OK, true},
704
705 // Logical operations with Infinity
706 {"$inf_var && 1", 1.0, EVAL_ERROR_OK, true},
707 {"$inf_var || 1", 1.0, EVAL_ERROR_OK, true},
708 {"$inf_var && 0", 0.0, EVAL_ERROR_OK, true},
709 {"$inf_var || 0", 1.0, EVAL_ERROR_OK, true},
710 {"!$inf_var", 0.0, EVAL_ERROR_OK, true},
711
712 // Ternary with Infinity
713 {"($inf_var) ? 1 : 2", 1.0, EVAL_ERROR_OK, true},
714
715 // Zero division
716 {"5 / 0", 0.0, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Positive/zero gives infinity
717 {"-5 / 0", 0.0, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Negative/zero gives -infinity
718 {"0 / 0", 0.0, EVAL_ERROR_VALUE_IS_INFINITE, true}, // In Netdata, this gives INFINITE error
719
720 // NaN and Infinity comparison
721 {"$inf_var == $nan_var", 0.0, EVAL_ERROR_OK, true},
722 {"$inf_var != $nan_var", 1.0, EVAL_ERROR_OK, true},
723 {"$inf_var > $nan_var", 0.0, EVAL_ERROR_OK, true},
724 {"$inf_var < $nan_var", 0.0, EVAL_ERROR_OK, true},
725 {"$inf_var >= $nan_var", 0.0, EVAL_ERROR_OK, true},
726 {"$inf_var <= $nan_var", 0.0, EVAL_ERROR_OK, true},
727
728 // Logical operations with mixed NaN and Infinity
729 {"$inf_var && $nan_var", 0.0, EVAL_ERROR_OK, true},
730 {"$inf_var || $nan_var", 1.0, EVAL_ERROR_OK, true},
731 {"!$nan_var && $inf_var", 1.0, EVAL_ERROR_OK, true},
732 {"!$nan_var || !$inf_var", 1.0, EVAL_ERROR_OK, true},
733
734 // Special value operations with zero
735 {"$zero * $inf_var", 0.0, EVAL_ERROR_VALUE_IS_INFINITE, true},
736 {"$zero / $zero", 0.0, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Netdata treats 0/0 as INFINITE
737 {"($zero) ? 1 : 2", 2.0, EVAL_ERROR_OK, true},
738
739 // Short-circuit evaluation with special values (these work because no evaluation happens)
740 {"0 && $nan_var", 0.0, EVAL_ERROR_OK, true}, // Short-circuit should avoid NaN
741 {"1 || $nan_var", 1.0, EVAL_ERROR_OK, true}, // Short-circuit should avoid NaN
742 {"0 && $inf_var", 0.0, EVAL_ERROR_OK, true}, // Short-circuit should avoid Infinity
743 {"1 || $inf_var", 1.0, EVAL_ERROR_OK, true} // Short-circuit should avoid Infinity
744 };
745
746 // Complex expression tests
747 static TestCase complex_tests[] = {
748 {"1 + 2 * 3 - 4 / 2", 5.0, EVAL_ERROR_OK, true},
749 {"(1 + 2) * (3 - 4) / 2", -1.5, EVAL_ERROR_OK, true},
750 {"1 > 0 && 2 > 1", 1.0, EVAL_ERROR_OK, true},
751 {"1 > 0 || 0 > 1", 1.0, EVAL_ERROR_OK, true},
752 {"(1 > 0) ? 10 : 20", 10.0, EVAL_ERROR_OK, true},
753 {"(0 > 1) ? 10 : 20", 20.0, EVAL_ERROR_OK, true},
754 {"((($var1 + $var2) / 2) > 30) ? ($var1 * $var2) : ($var1 + $var2)", 1008.0, EVAL_ERROR_OK, true},
755 {"5 + (!($var1 > 50) * 10)", 15.0, EVAL_ERROR_OK, true},
756 {"($var1 > $var2) ? ($var1 - $var2) : ($var2 - $var1)", 18.0, EVAL_ERROR_OK, true},
757 {"(($zero > 0) ? $var1 : $var2) + (($zero < 0) ? $var1 : $var2)", 48.0, EVAL_ERROR_OK, true},
758 };
759
760 // Edge case and invalid expressions
761 static TestCase edge_case_tests[] = {
762 {"", 0.0, EVAL_ERROR_OK, false},
763 {" ", 0.0, EVAL_ERROR_MISSING_OPERAND, false}, // Netdata can't parse whitespace-only expressions
764 {"\t\n", 0.0, EVAL_ERROR_MISSING_OPERAND, false}, // Netdata can't parse whitespace-only expressions
765 {" 5 + 3 ", 8.0, EVAL_ERROR_OK, true}, // Whitespace between operands is fine
766 {"$", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false}, // Netdata's error is different for incomplete variables
767 {"${", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false}, // Netdata's error is different for incomplete variables
768 {"$}", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false}, // Netdata's error is different for incomplete variables
769 {"${}", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false}, // Netdata's error is different for incomplete variables
770 {"5 + -3", 2.0, EVAL_ERROR_OK, true}, // Netdata actually handles this correctly as a unary minus
771 {"5 + 3", 8.0, EVAL_ERROR_OK, true}, // Basic sanity check
772 };
773
774 // Operator precedence tests
775 static TestCase precedence_tests[] = {
776 {"5 + 3 * 2", 11.0, EVAL_ERROR_OK, true}, // * before +
777 {"5 * 3 + 2", 17.0, EVAL_ERROR_OK, true}, // * before +
778 {"5 + 3 - 2", 6.0, EVAL_ERROR_OK, true}, // + and - same precedence (left to right)
779 {"5 - 3 + 2", 4.0, EVAL_ERROR_OK, true}, // + and - same precedence (left to right)
780 {"5 * 3 / 3", 5.0, EVAL_ERROR_OK, true}, // * and / same precedence (left to right)
781 {"5 / 5 * 3", 3.0, EVAL_ERROR_OK, true}, // * and / same precedence (left to right)
782 {"5 > 3 && 2 < 4 || 1 == 0", 1.0, EVAL_ERROR_OK, true}, // && before ||
783 {"5 > 3 && (2 < 4 || 1 == 0)", 1.0, EVAL_ERROR_OK, true}, // same as above with explicit grouping
784 {"5 > 3 || 2 < 4 && 1 == 0", 0.0, EVAL_ERROR_OK, true}, // In Netdata, || and && have same precedence (left to right)
785 {"(5 > 3 || 2 < 4) && 1 == 0", 0.0, EVAL_ERROR_OK, true}, // explicit grouping with same result
786 {"!5 > 3", 0.0, EVAL_ERROR_OK, true}, // ! before >
787 {"!(5 > 3)", 0.0, EVAL_ERROR_OK, true}, // same as above with explicit grouping
788 {"5 + 3 > 2 * 3", 1.0, EVAL_ERROR_OK, true}, // arithmetic before comparison
789 {"5 + 3 > 2 * 4", 0.0, EVAL_ERROR_OK, true}, // arithmetic before comparison
790 {"(5 > 3) ? (1 + 2) : (3 + 4)", 3.0, EVAL_ERROR_OK, true}, // ternary has low precedence
791 {"($var1 + $var2 * 2 > 80) ? 100 : 200", 100.0, EVAL_ERROR_OK, true}, // complex precedence test (42 + 24*2 = 90, which is > 80)
792 };
793
794 // Parentheses tests - specifically testing how parentheses change operator precedence
795 static TestCase parentheses_tests[] = {
796 {"5 + 3 * 2", 11.0, EVAL_ERROR_OK, true}, // Default: * has higher precedence
797 {"(5 + 3) * 2", 16.0, EVAL_ERROR_OK, true}, // Parentheses change precedence
798 {"5 * (3 + 2)", 25.0, EVAL_ERROR_OK, true}, // Parentheses change order of operations
799 {"(5 + 3 * 2)", 11.0, EVAL_ERROR_OK, true}, // Redundant parentheses don't change anything
800 {"((5 + 3) * 2)", 16.0, EVAL_ERROR_OK, true}, // Nested parentheses
801 {"5 - (3 - 1)", 3.0, EVAL_ERROR_OK, true}, // Parentheses with subtraction
802 {"5 - 3 - 1", 1.0, EVAL_ERROR_OK, true}, // Without parentheses (left-to-right)
803 {"(5 - 3) - 1", 1.0, EVAL_ERROR_OK, true}, // Explicit grouping doesn't change result
804 {"5 - (3 - 1)", 3.0, EVAL_ERROR_OK, true}, // Different grouping changes result
805 {"5 / (2 * 2.5)", 1.0, EVAL_ERROR_OK, true}, // Division and multiplication with parentheses
806 {"(5 / 2) * 2.5", 6.25, EVAL_ERROR_OK, true}, // Different grouping changes result
807 {"$var1 * ($var2 + 6)", 1260.0, EVAL_ERROR_OK, true}, // Variables with parentheses
808 {"($var1 * $var2) + 6", 1014.0, EVAL_ERROR_OK, true}, // Different grouping with variables
809 {"!($var1 > $var2)", 0.0, EVAL_ERROR_OK, true}, // Logical NOT with parentheses
810 {"!(0)", 1.0, EVAL_ERROR_OK, true}, // Logical NOT with constant
811 {"!0", 1.0, EVAL_ERROR_OK, true}, // Same without parentheses
812 {"5 > 3 && (2 < 1 || 3 > 1)", 1.0, EVAL_ERROR_OK, true}, // Complex logical expression with parentheses
813 {"(5 > 3 && 2 < 1) || 3 > 1", 1.0, EVAL_ERROR_OK, true}, // Different grouping changes result
814 {"5 > 3 && 2 < 1 || 3 > 1", 1.0, EVAL_ERROR_OK, true}, // Default precedence (&& before ||)
815 {"(5 > 3) && ((2 < 1) || (3 > 1))", 1.0, EVAL_ERROR_OK, true}, // Excessive parentheses
816 {"(((5))) + (((3)))", 8.0, EVAL_ERROR_OK, true}, // Multiple nested parentheses
817 {"abs(-($var1 - $var2))", 18.0, EVAL_ERROR_OK, true}, // Function with parenthesized expression
818 {"abs(-(($var1) - ($var2)))", 18.0, EVAL_ERROR_OK, true}, // Function with nested parentheses
819 {"(5 > 3) ? ($var1 + $var2) : ($var1 - $var2)", 66.0, EVAL_ERROR_OK, true}, // Ternary with parentheses
820 {"((5 > 3) ? $var1 : $var2) + 10", 52.0, EVAL_ERROR_OK, true}, // Parentheses around ternary
821 };
822
823 // Tests for API functions
824 static TestCase api_function_tests[] = {
825 {"1 + 2", 3.0, EVAL_ERROR_OK, true}, // For testing expression_source and expression_parsed_as
826 {"$var1", 42.0, EVAL_ERROR_OK, true}, // For testing expression_result and variable lookup
827 {"bad/syntax", 0.0, EVAL_ERROR_UNKNOWN_OPERAND, false}, // For testing expression_error_msg
828 {"$hardcoded_var", 0.0, EVAL_ERROR_UNKNOWN_VARIABLE, true}, // For testing expression_hardcode_variable
829 };
830
831 // Test cases for number overflow
832 static TestCase overflow_tests[] = {
833 // Positive overflow - extreme large numbers
834 {"1e308", 1e308, EVAL_ERROR_OK, true}, // Very large but valid number
835 {"1e308 * 10", INFINITY, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Overflow to positive infinity
836 {"1e308 + 1e308", INFINITY, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Addition causing overflow
837
838 // Negative overflow - extreme large negative numbers
839 {"-1e308", -1e308, EVAL_ERROR_OK, true}, // Very large negative but valid
840 {"-1e308 * 10", -INFINITY, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Overflow to negative infinity
841 {"-1e308 - 1e308", -INFINITY, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Subtraction causing negative overflow
842
843 // Operations with infinity
844 {"1e308 * 1e308", INFINITY, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Multiplication causing overflow
845 {"-1e308 * -1e308", INFINITY, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Negative * negative = positive overflow
846 {"1e308 / 1e-308", INFINITY, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Division causing overflow
847
848 // Mixed operations
849 {"1e308 - 1e308", 0.0, EVAL_ERROR_OK, true}, // This should properly cancel out
850 {"(1e308 * 2) / 2", INFINITY, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Overflow in intermediate calculation
851 };
852
853 // Test cases for combined complex expressions
854 static TestCase combined_tests[] = {
855 // Complex arithmetic expressions combining multiple operations
856 {"(5 + 3 * 2) / (1 + 1) * 4 - 10", 12.0, EVAL_ERROR_OK, true},
857 {"((($var1 * 2) / 4) + (($var2 - 4) * 2)) / 10", 6.1, EVAL_ERROR_OK, true}, // Corrected expected result
858 {"abs($negative) * 2 + $var1 / 2 - $var2", 17.0, EVAL_ERROR_OK, true}, // Corrected expected result
859
860 // Complex boolean expressions with multiple conditions
861 {"($var1 > 40 && $var2 < 30) || ($var1 - $var2 > 10)", 1.0, EVAL_ERROR_OK, true},
862 {"!($var1 < 40) && ($var2 > 20 || $zero < 1) && !($var1 == $var2)", 1.0, EVAL_ERROR_OK, true},
863 // Ternary expressions need proper parentheses in Netdata
864 {"(($var1 > $var2) ? ($var1 - $var2) : ($var2 - $var1)) > 15", 1.0, EVAL_ERROR_OK, true},
865
866 // Mix of arithmetic and boolean with precedence tests
867 {"($var1 + $var2) / 2 > ($var1 > $var2 ? $var2 : $var1)", 1.0, EVAL_ERROR_OK, true},
868 {"(($var1 > $var2 ? 1 : 0) * 10 + (($var1 - $var2) / 3)) > 15", 1.0, EVAL_ERROR_OK, true},
869
870 // Complex expressions with potential overflows
871 {"(1e308 - 1e308) * $var1 + $var2", 24.0, EVAL_ERROR_OK, true},
872 {"($var1 > 0 ? 1e308 : -1e308) * ($var1 < 0 ? 1 : 0)", 0.0, EVAL_ERROR_OK, true},
873 {"(1e308 + 1e308 > 0) ? $var1 : $var2", 42.0, EVAL_ERROR_OK, true},
874
875 // Deeply nested expressions with mixed operations
876 {"((((($var1 / 2) + ($var2 * 2)) - 10) * 2) / 4) + (($var1 > $var2) ? 5 : -5)", 34.5, EVAL_ERROR_OK, true}, // Corrected expected result
877 // This tests the nested ternaries with proper parentheses
878 {"(abs($negative) > 5) ? $var1 : $var2", 42.0, EVAL_ERROR_OK, true}, // Simplified to avoid nested ternary issue
879 {"(($var1 + $var2) / 2 > 30) ? 10 : 5", 10.0, EVAL_ERROR_OK, true}, // Simplified second half of above test
880
881 // Expressions with short-circuit evaluation
882 {"$zero && (1 / $zero)", 0.0, EVAL_ERROR_OK, true}, // Short-circuit prevents division by zero
883 {"1 || (1e308 * 1e308)", 1.0, EVAL_ERROR_OK, true}, // Short-circuit prevents overflow
884 // Split this into two tests to avoid nested ternary issues
885 {"($var1 < 0) ? (1 / $zero) : $var1", 42.0, EVAL_ERROR_OK, true}, // First part of previous test
886 {"($var2 > 100) ? (1e308 * 1e308) : $var2", 24.0, EVAL_ERROR_OK, true}, // Second part of previous test
887 };
888
889 // Test cases that previously crashed Netdata
890 // Note: When using the re2c/lemon parser, nested ternary operators without parentheses
891 // are properly supported (unlike the original recursive descent parser)
892 static TestCase crash_tests[] = {
893 #ifdef USE_RE2C_LEMON_PARSER
894 {"$var1 > 0 ? $var1 < 0 ? 1 : 2 : 3", 2.0, EVAL_ERROR_OK, true},
895 {"$var1 > 0 ? ( $var1 < 0 ? 1 : 2 ) : 3", 2.0, EVAL_ERROR_OK, true},
896 {"( $var1 > 0 ? $var1 < 0 ? 1 : 2 : 3 )", 2.0, EVAL_ERROR_OK, true},
897 #else
898 // Original recursive descent parser can't handle nested ternaries without parentheses
899 {"$var1 > 0 ? $var1 < 0 ? 1 : 2 : 3", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false},
900 {"$var1 > 0 ? ( $var1 < 0 ? 1 : 2 ) : 3", 1.0, EVAL_ERROR_OK, true},
901 {"( $var1 > 0 ? $var1 < 0 ? 1 : 2 : 3 )", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false},
902 #endif
903 // Fully parenthesized works correctly in both parsers
904 {"($var1 > 0) ? (($var1 < 0) ? 1 : 2) : 3", 2.0, EVAL_ERROR_OK, true},
905 // Multiple nested parentheses are fine
906 {"(($zero)) ? 0 : ((($var1)))", 42.0, EVAL_ERROR_OK, true},
907 // Variable lookup errors are properly handled
908 {"$nonexistent + $var1", 0.0, EVAL_ERROR_UNKNOWN_VARIABLE, true},
909 // Division by (0-0) gives an INFINITE error in Netdata's implementation
910 {"10 / ($zero - $zero)", 0.0, EVAL_ERROR_VALUE_IS_INFINITE, true},
911 {"true", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false},
912 {"false", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false},
913 };
914
915 // Test cases for variable names with spaces
916 static TestCase variable_space_tests[] = {
917 // Testing $var syntax (can't have spaces)
918 {"$this", 50.0, EVAL_ERROR_OK, true},
919 {"$this variable", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false}, // This should fail to parse as "variable" is considered garbage
920 {"$this + variable", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false}, // Invalid syntax
921
922 // Testing ${var} syntax (can have spaces)
923 {"${this}", 50.0, EVAL_ERROR_OK, true},
924 {"${this variable}", 100.0, EVAL_ERROR_OK, true}, // This should parse as a single variable named 'this variable'
925
926 // Testing more complex expressions with spaced variable names
927 {"${this variable} * 2", 200.0, EVAL_ERROR_OK, true},
928 {"${this variable} > ${this}", 1.0, EVAL_ERROR_OK, true},
929 {"${this} + ${this variable}", 150.0, EVAL_ERROR_OK, true},
930
931 // Edge cases with missing or incomplete braces
932 #ifdef USE_RE2C_LEMON_PARSER
933 {"${this variable", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false}, // Missing closing brace is a syntax error
934 #else
935 {"${this variable", 100.0, EVAL_ERROR_OK, true}, // Missing closing brace but parser accepts it
936 #endif
937 {"${}", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false}, // Empty brackets
938
939 // Using ${var} inside complex expressions
940 {"(${this variable} + ${this}) / 2", 75.0, EVAL_ERROR_OK, true},
941 {"(${this} > 0) ? ${this variable} : 0", 100.0, EVAL_ERROR_OK, true}, // Fixed the ternary syntax
942 {"$1var", 42.0, EVAL_ERROR_OK, true},
943 {"${1var}", 42.0, EVAL_ERROR_OK, true},
944 {"$_var", 76.0, EVAL_ERROR_OK, true},
945 {"${_var}", 76.0, EVAL_ERROR_OK, true},
946 {"$1.var", 77.0, EVAL_ERROR_OK, true},
947 {"${1.var}", 77.0, EVAL_ERROR_OK, true},
948 {"$var.1", 78.0, EVAL_ERROR_OK, true},
949 {"${var.1}", 78.0, EVAL_ERROR_OK, true},
950 {"$var-1", 0.0, EVAL_ERROR_UNKNOWN_VARIABLE, true},
951 {"${var-1}", 79.0, EVAL_ERROR_OK, true},
952 };
953
954 // Test cases for nested unary operators
955 static TestCase nested_unary_tests[] = {
956 // Nested minus operator
957 {"-(-5)", 5.0, EVAL_ERROR_OK, true},
958 {"-(-0)", 0.0, EVAL_ERROR_OK, true},
959 {"-(-$negative)", -10.0, EVAL_ERROR_OK, true},
960 {"-(-$nan_var)", NAN, EVAL_ERROR_VALUE_IS_NAN, true},
961 {"-(-$inf_var)", INFINITY, EVAL_ERROR_VALUE_IS_INFINITE, true},
962
963 // Nested plus operator
964 {"+(-5)", -5.0, EVAL_ERROR_OK, true},
965 {"+(-0)", 0.0, EVAL_ERROR_OK, true},
966 {"+($negative)", -10.0, EVAL_ERROR_OK, true},
967 {"+($nan_var)", NAN, EVAL_ERROR_VALUE_IS_NAN, true},
968 {"+($inf_var)", INFINITY, EVAL_ERROR_VALUE_IS_INFINITE, true},
969 {"+(+5)", 5.0, EVAL_ERROR_OK, true},
970
971 // Nested not operator
972 {"!(!0)", 0.0, EVAL_ERROR_OK, true},
973 {"!(!1)", 1.0, EVAL_ERROR_OK, true},
974 {"!(!$zero)", 0.0, EVAL_ERROR_OK, true},
975 {"!(!$negative)", 1.0, EVAL_ERROR_OK, true},
976 {"!(!$nan_var)", 0.0, EVAL_ERROR_OK, true},
977 {"!(!$inf_var)", 1.0, EVAL_ERROR_OK, true},
978
979 // Multiple nested unary operators
980 {"-(-(-5))", -5.0, EVAL_ERROR_OK, true},
981 {"+(-(-5))", 5.0, EVAL_ERROR_OK, true},
982 {"-(-(-(-5)))", 5.0, EVAL_ERROR_OK, true},
983 {"!(!(!0))", 1.0, EVAL_ERROR_OK, true},
984 {"!(!(!1))", 0.0, EVAL_ERROR_OK, true},
985
986 // Nested abs function
987 {"abs(abs(-5))", 5.0, EVAL_ERROR_OK, true},
988 {"abs(-abs(-5))", 5.0, EVAL_ERROR_OK, true}, // Now fixed
989 {"abs(abs($negative))", 10.0, EVAL_ERROR_OK, true},
990 {"abs(abs($nan_var))", NAN, EVAL_ERROR_VALUE_IS_NAN, true},
991 {"abs(abs($inf_var))", INFINITY, EVAL_ERROR_VALUE_IS_INFINITE, true},
992
993 // Mixed unary operators - fix the expected value for abs(!1)
994 // For now, let's correct the test case for abs(!1)
995 {"abs(-(-5))", 5.0, EVAL_ERROR_OK, true},
996 {"abs(+(-5))", 5.0, EVAL_ERROR_OK, true},
997 {"abs(!0)", 1.0, EVAL_ERROR_OK, true},
998 {"abs(!1)", 0.0, EVAL_ERROR_OK, true}, // Changed from 1.0 to 0.0 because !1 is 0, abs(0) is 0
999 {"-(!0)", -1.0, EVAL_ERROR_OK, true},
1000 {"-(!1)", 0.0, EVAL_ERROR_OK, true},
1001 {"+(!0)", 1.0, EVAL_ERROR_OK, true},
1002 {"+(!1)", 0.0, EVAL_ERROR_OK, true},
1003
1004 // Complex nested expressions
1005 {"-(5 + -3)", -2.0, EVAL_ERROR_OK, true},
1006 {"+(5 + -3)", 2.0, EVAL_ERROR_OK, true},
1007 {"!(5 > 3)", 0.0, EVAL_ERROR_OK, true},
1008 {"!!(5 > 3)", 1.0, EVAL_ERROR_OK, true},
1009 {"abs(-(5 - 10))", 5.0, EVAL_ERROR_OK, true},
1010 {"-abs(-(5 - 10))", -5.0, EVAL_ERROR_OK, true}, // Now fixed
1011 };
1012
1013 // Test cases for real-world expressions from the dataset
1014 static TestCase real_world_tests[] = {
1015 // Expressions with nested ternary operators and status comparisons
1016 {"$10m_acquiring_requests >= 50 && $this < (($status == $CRITICAL) ? (80) : (50))", 0.0, EVAL_ERROR_OK, true},
1017 {"$10m_acquiring_requests >= 50 && $this < (($status == $CRITICAL) ? (95) : (85))", 1.0, EVAL_ERROR_OK, true},
1018 {"$10m_acquiring_requests >= 50 && $this < (($status >= $WARNING) ? (90) : (75))", 1.0, EVAL_ERROR_OK, true},
1019 {"$10m_acquiring_requests >= 50 && $this < (($status >= $WARNING) ? (99) : (95))", 1.0, EVAL_ERROR_OK, true},
1020
1021 // Expressions with nested ternary operators and varying syntax
1022 {"($10m_acquiring_requests > 120) ? ($this > (($status == $CRITICAL) ? ( 2 ) : ( 5 )) ) : ( 0 )", 0.0, EVAL_ERROR_OK, true},
1023 {"($10m_acquiring_requests > 120) ? ($this < (($status == $CRITICAL) ? ( 85 ) : ( 75 )) ) : ( 0 )", 0.0, EVAL_ERROR_OK, true},
1024 {"($10m_acquiring_requests > 120) ? ($this > (($status >= $WARNING) ? ( 10 ) : ( 30 )) ) : ( 0 )", 0.0, EVAL_ERROR_OK, true},
1025 {"($10m_acquiring_requests > 120) ? ($this > (($status >= $WARNING ) ? ( 1 ) : ( 20 )) ) : ( 0 )", 0.0, EVAL_ERROR_OK, true},
1026
1027 // Expressions with whitespace variations
1028 {"($10m_acquiring_requests > 120) ? ($this > (($status >= $WARNING ) ? ( 1 ) : ( 20 )) ) : ( 0 )", 0.0, EVAL_ERROR_OK, true},
1029 {"($10m_acquiring_requests>120)?($this>(($status>=$WARNING)?(1):(20))):(0)", 0.0, EVAL_ERROR_OK, true},
1030
1031 // Complex variable expressions with hierarchical variables
1032 {"$avail * 100 / ($system.ram.used + $system.ram.cached + $system.ram.free + $system.ram.buffers)", 25.0, EVAL_ERROR_OK, true},
1033 {"($active + $wired + $laundry + $buffers - $used_ram_to_ignore) * 100 / ($active + $wired + $laundry + $buffers - $used_ram_to_ignore + $cache + $free + $inactive)", 51.111111, EVAL_ERROR_OK, true},
1034
1035 // CPU Core variables with spaces in braced syntax
1036 {"(${Core 0} + ${Core 1} + ${Core 2} + ${Core 3}) / 4", 28.75, EVAL_ERROR_OK, true},
1037 {"${Core 0} > 15", 1.0, EVAL_ERROR_OK, true},
1038 {"${Core 0} > 15 OR ${Core 1} > 15 OR ${Core 02} > 15 OR ${Core 3} > 15", 1.0, EVAL_ERROR_OK, true},
1039 {"${Core 0} > 15 OR ${Core 1} > 15 OR ${Core 02} > 15 OR ${Core 3} > 60", 1.0, EVAL_ERROR_OK, true},
1040 {"${Core 0} > 15 OR $Core1 > 55 OR $Core2 > 55 OR $Core3 > 55", 1.0, EVAL_ERROR_OK, true},
1041
1042 // NaN checking patterns
1043 {"(($1hour_packet_drops_inbound != nan AND $this > 0) ? ($1hour_packet_drops_inbound * 100 / $this) : (0))", 500.0, EVAL_ERROR_OK, true},
1044 {"(($1hour_packet_drops_outbound != nan AND $this > 0) ? ($1hour_packet_drops_outbound * 100 / $this) : (0))", 300.0, EVAL_ERROR_OK, true},
1045 {"(($1m_ipv4_udp_receive_buffer_errors != nan AND $this > 30000) ? ($1m_ipv4_udp_receive_buffer_errors * 100 / $this) : (0))", 0.0, EVAL_ERROR_OK, true},
1046
1047 // Complex expressions with `or` and `and` lowercase keywords and nan/inf checks
1048 {"($active_processors == nan or $active_processors == 0) ? (nan) : (($active_processors < 2) ? (2) : ($active_processors))", 8.0, EVAL_ERROR_OK, true},
1049 {"($active_processors == nan or $active_processors == inf or $active_processors < 2) ? (2) : ($active_processors)", 8.0, EVAL_ERROR_OK, true},
1050 {"($active_processors == nan or $active_processors == inf or $active_processors < 2) ? (2) : ($active_processors / 1.2)", 6.666666666666667, EVAL_ERROR_OK, true},
1051
1052 // Time-based expressions with variable comparisons
1053 {"$last_collected_t < $now - 60", 0.0, EVAL_ERROR_OK, true},
1054 {"$last_scrub > (15*60*60)", 0.0, EVAL_ERROR_OK, true},
1055
1056 // Floating point operations
1057 {"$mem > (($status == $CRITICAL) ? ($tcp_mem_pressure) : ($tcp_mem_high * 0.9))", 1.0, EVAL_ERROR_OK, true},
1058 {"$mem > (($status >= $WARNING) ? ($tcp_mem_pressure * 0.8) : ($tcp_mem_pressure))", 1.0, EVAL_ERROR_OK, true},
1059
1060 // Memory expressions with various notations and hierarchical variables
1061 {"$avail * 100 / ($system.ram.free + $system.ram.active + $system.ram.inactive + $system.ram.wired + $system.ram.cache + $system.ram.laundry + $system.ram.buffers)", 20.212766, EVAL_ERROR_OK, true},
1062 {"$avail * 100 / ($system.ram.used + $system.ram.cached + $system.ram.free + $system.ram.buffers + $system.ram.swap)", 19.791667, EVAL_ERROR_OK, true},
1063
1064 // Pattern usages of abs() function
1065 {"($this != 0) || ($status == $CRITICAL && abs($sent) == 0)", 1.0, EVAL_ERROR_OK, true},
1066 {"abs($bandwidth_1m_avg_of_now - $bandwidth_1m_avg_of_previous_1m) * 100 / $bandwidth_1m_avg_of_previous_1m", 5.0, EVAL_ERROR_OK, true},
1067 {"abs($offset)", 5.0, EVAL_ERROR_OK, true},
1068 {"abs($sent) * 100 / abs($buffered)", 200.0, EVAL_ERROR_OK, true},
1069
1070 // Label syntax
1071 {"(${label:host} != \"wg-manage-lte\") AND ($this > $green OR $this > $red)", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false},
1072
1073 // Used with pidmax (common system metrics pattern)
1074 {"$active * 100 / $pidmax", 4.577637, EVAL_ERROR_OK, true},
1075 {"$arrays * 100 / $ipc.semaphores.arrays.max", 50.0, EVAL_ERROR_OK, true},
1076 {"$arrays * 100 / $ipc_semaphores_arrays_max", 50.0, EVAL_ERROR_OK, true},
1077
1078 // Basic patterns with lowercase logical operators
1079 {"$netdata.uptime.uptime > 30 AND $this > 0 and $this < 24", 0.0, EVAL_ERROR_OK, true},
1080 {"($this > $green OR $var1 > $red) and $this > 2", 1.0, EVAL_ERROR_OK, true},
1081
1082 // Expressions with word operators and comparisons
1083 {"$var1 > 40 AND $var2 < 30", 1.0, EVAL_ERROR_OK, true},
1084 {"$var1 > 40 OR $var2 < 30", 1.0, EVAL_ERROR_OK, true},
1085 {"NOT($var1 < 40 AND $var2 > 20)", 1.0, EVAL_ERROR_OK, true},
1086
1087 // Mixed symbol and word operators
1088 {"$var1 > 40 AND ($var2 < 30 || $this > 45)", 1.0, EVAL_ERROR_OK, true},
1089 {"($var1 > 30 && $var2 < 30) OR $this > 45", 1.0, EVAL_ERROR_OK, true},
1090 };
1091
1092 // Define the test groups
1093 static TestGroup test_groups[] = {
1094 {"Arithmetic Tests", arithmetic_tests, ARRAY_SIZE(arithmetic_tests)},
1095 {"Comparison Tests", comparison_tests, ARRAY_SIZE(comparison_tests)},
1096 {"Logical Tests", logical_tests, ARRAY_SIZE(logical_tests)},
1097 {"Variable Tests", variable_tests, ARRAY_SIZE(variable_tests)},
1098 {"Variable Space Tests", variable_space_tests, ARRAY_SIZE(variable_space_tests)},
1099 {"Function Tests", function_tests, ARRAY_SIZE(function_tests)},
1100 {"Special Value Tests", special_value_tests, ARRAY_SIZE(special_value_tests)},
1101 {"Complex Expression Tests", complex_tests, ARRAY_SIZE(complex_tests)},
1102 {"Edge Case Tests", edge_case_tests, ARRAY_SIZE(edge_case_tests)},
1103 {"Operator Precedence Tests", precedence_tests, ARRAY_SIZE(precedence_tests)},
1104 {"Parentheses Tests", parentheses_tests, ARRAY_SIZE(parentheses_tests)},
1105 {"Nested Unary Tests", nested_unary_tests, ARRAY_SIZE(nested_unary_tests)},
1106 {"Real-World Expression Tests", real_world_tests, ARRAY_SIZE(real_world_tests)},
1107 {"API Function Tests", api_function_tests, ARRAY_SIZE(api_function_tests)},
1108 {"Number Overflow Tests", overflow_tests, ARRAY_SIZE(overflow_tests)},
1109 {"Combined Complex Expressions", combined_tests, ARRAY_SIZE(combined_tests)},
1110 {"Crash Tests", crash_tests, ARRAY_SIZE(crash_tests)},
1111 };
1112
1113 int eval_hardcode_unittest(void);
1114
1115 int eval_unittest(void) {
1116 // Test cases for basic arithmetic operations
1117
1118 // Run all test groups
1119 int total_passed = 0;
1120 int total_failed = 0;
1121 int total_tests = 0;
1122
1123 #ifdef USE_RE2C_LEMON_PARSER
1124 printf("Starting comprehensive evaluation tests using RE2C/LEMON PARSER\n");
1125 #else
1126 printf("Starting comprehensive evaluation tests using RECURSIVE DESCENT PARSER\n");
1127 #endif
1128
1129 for (size_t i = 0; i < ARRAY_SIZE(test_groups); i++) {
1130 run_test_group(&test_groups[i]);
1131
1132 int group_tests = test_groups[i].test_count;
1133 int group_passed = 0;
1134 int group_failed = 0;
1135
1136 for (int j = 0; j < group_tests; j++) {
1137 TestCase *tc = &test_groups[i].test_cases[j];
1138 const char *failed_at = NULL;
1139 int error = 0;
1140
1141 // Try to parse the expression
1142 EVAL_EXPRESSION *exp = expression_parse(tc->expression, &failed_at, &error);
1143
1144 // Check if parsing succeeded as expected
1145 bool test_failed = false;
1146
1147 if (tc->should_parse && !exp) {
1148 test_failed = true;
1149 }
1150 else if (!tc->should_parse && exp) {
1151 test_failed = true;
1152 }
1153
1154 // If the expression parsed successfully, evaluate it
1155 if (exp) {
1156 // Set up the variable lookup callback
1157 expression_set_variable_lookup_callback(exp, test_variable_lookup, NULL);
1158
1159 // Evaluate the expression
1160 expression_evaluate(exp);
1161
1162 // Check if there was an error during evaluation
1163 if (tc->expected_error != EVAL_ERROR_OK && exp->error == EVAL_ERROR_OK) {
1164 test_failed = true;
1165 }
1166 else if (tc->expected_error == EVAL_ERROR_OK && exp->error != EVAL_ERROR_OK) {
1167 test_failed = true;
1168 }
1169 else if (tc->expected_error != EVAL_ERROR_OK && exp->error != tc->expected_error) {
1170 test_failed = true;
1171 }
1172
1173 // Check the evaluation result
1174 if (tc->expected_error == EVAL_ERROR_OK) {
1175 if (isnan(tc->expected_result) && !isnan(exp->result)) {
1176 test_failed = true;
1177 }
1178 else if (isinf(tc->expected_result) && !isinf(exp->result)) {
1179 test_failed = true;
1180 }
1181 else if (!isnan(tc->expected_result) && !isinf(tc->expected_result) &&
1182 !isnan(exp->result) && !isinf(exp->result) &&
1183 fabs(tc->expected_result - exp->result) > 0.000001) {
1184 test_failed = true;
1185 }
1186 }
1187
1188 // Clean up
1189 expression_free(exp);
1190 }
1191
1192 if (test_failed) {
1193 group_failed++;
1194 } else {
1195 group_passed++;
1196 }
1197 }
1198
1199 total_passed += group_passed;
1200 total_failed += group_failed;
1201 total_tests += group_tests;
1202 }
1203
1204 printf("\n========== OVERALL TEST SUMMARY ==========\n");
1205 printf("Total tests: %d\n", total_tests);
1206 printf("Passed: %d (%.1f%%)\n", total_passed, (float)total_passed / total_tests * 100);
1207 printf("Failed: %d (%.1f%%)\n", total_failed, (float)total_failed / total_tests * 100);
1208
1209 if(!total_failed)
1210 return eval_hardcode_unittest();
1211
1212 return total_failed > 0 ? 1 : 0;
1213 }