master
c 244 lines 7.81 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "libnetdata/libnetdata.h"
4 #include "eval-internal.h"
5
6 // Test case structure for hardcode variable tests
7 typedef struct {
8 const char *name; // Name/description of the test case
9 const char *expression; // Initial expression to parse
10 const char *variable; // Variable name to hardcode (NULL for testing NULL variable)
11 NETDATA_DOUBLE hardcode_value; // Value to hardcode (NAN for testing NaN)
12 const char *expected_source; // Expected expression source after hardcoding
13 NETDATA_DOUBLE expected_result; // Expected result after evaluation
14 int expected_error; // Expected error code (EVAL_ERROR_OK if no error)
15 } HardcodeTestCase;
16
17 int eval_hardcode_unittest(void) {
18 printf("\n=== Running Tests for expression_hardcode_variable() ===\n");
19
20 // Define all test cases as data
21 HardcodeTestCase test_cases[] = {
22 // Basic variable replacement
23 {
24 "Basic variable",
25 "$test_var + 10",
26 "test_var",
27 42.0,
28 "42 + 10",
29 52.0,
30 EVAL_ERROR_OK
31 },
32 // Variable with braces
33 {
34 "Variable with braces",
35 "${test_var} * 2",
36 "test_var",
37 42.0,
38 "42 * 2",
39 84.0,
40 EVAL_ERROR_OK
41 },
42 // Multiple occurrences of same variable
43 {
44 "Multiple occurrences",
45 "$test_var + ${test_var} + $test_var + ${test_var} + $test_var + ${test_var}",
46 "test_var",
47 42.0,
48 "42 + 42 + 42 + 42 + 42 + 42",
49 252.0,
50 EVAL_ERROR_OK
51 },
52 // Complex nested expression
53 {
54 "Complex expression",
55 "($test_var > 30) ? (${test_var} * 2) : ($test_var / 2)",
56 "test_var",
57 42.0,
58 "(42 > 30) ? (42 * 2) : (42 / 2)",
59 84.0,
60 EVAL_ERROR_OK
61 },
62 // Variable not in expression (should remain unchanged)
63 {
64 "Variable not in expression",
65 "33 + 33",
66 "test_var",
67 42.0,
68 "33 + 33",
69 66.0,
70 EVAL_ERROR_OK
71 },
72 // Hardcoding negative value
73 {
74 "Negative value",
75 "$test_var * 10",
76 "test_var",
77 -5.0,
78 "-5 * 10",
79 -50.0,
80 EVAL_ERROR_OK
81 },
82 // Hardcoding decimal value
83 {
84 "Decimal value",
85 "$test_var / 10",
86 "test_var",
87 123.456,
88 "123.456 / 10",
89 12.3456,
90 EVAL_ERROR_OK
91 },
92 // Function parameter
93 {
94 "Function parameter",
95 "abs($test_var)",
96 "test_var",
97 -42.0,
98 "abs(-42)",
99 42.0,
100 EVAL_ERROR_OK
101 },
102 // NaN value
103 {
104 "NaN value (ignored)",
105 "$test_var + 10",
106 "test_var",
107 NAN,
108 "nan + 10", // source should remain unchanged
109 0.0, // Unresolved variable error
110 EVAL_ERROR_VALUE_IS_NAN
111 },
112 // INFINITY value
113 {
114 "NaN value (ignored)",
115 "$test_var + 10",
116 "test_var",
117 INFINITY,
118 "inf + 10", // source should remain unchanged
119 0.0, // Unresolved variable error
120 EVAL_ERROR_VALUE_IS_INFINITE
121 },
122 // NULL expression (no crash test)
123 {
124 "NULL expression",
125 NULL, // This isn't actually used - we'll handle it specially
126 "test_var",
127 42.0,
128 NULL, // Expected source doesn't matter
129 0.0, // Result doesn't matter
130 EVAL_ERROR_OK
131 },
132 // NULL variable (no crash test)
133 {
134 "NULL variable",
135 "$test_var + 10",
136 NULL, // NULL variable name
137 42.0,
138 "$test_var + 10", // source should remain unchanged
139 0.0, // Unresolved variable error
140 EVAL_ERROR_UNKNOWN_VARIABLE
141 }
142 };
143
144 int passed = 0;
145 int failed = 0;
146
147 // Single loop to process all test cases
148 for (size_t i = 0; i < sizeof(test_cases) / sizeof(test_cases[0]); i++) {
149 HardcodeTestCase *tc = &test_cases[i];
150
151 printf("Test %zu: %s\n", i + 1, tc->name);
152
153 // Handle special case of NULL expression test (just don't crash)
154 if (!tc->expression) {
155 printf(" Testing NULL expression (shouldn't crash)...\n");
156 STRING *var = tc->variable ? string_strdupz(tc->variable) : NULL;
157
158 // This call shouldn't crash
159 expression_hardcode_variable(NULL, var, tc->hardcode_value);
160
161 printf(" PASSED: No crash with NULL expression\n");
162 passed++;
163 if (var) string_freez(var);
164 continue;
165 }
166
167 // Parse the expression
168 const char *failed_at = NULL;
169 int error = 0;
170 EVAL_EXPRESSION *exp = expression_parse(tc->expression, &failed_at, &error);
171
172 if (!exp) {
173 printf(" FAILED: Could not parse expression, error: %d (%s)\n",
174 error, expression_strerror(error));
175 failed++;
176 continue;
177 }
178
179 // Save the original source
180 const char *original_source = expression_source(exp);
181 printf(" Original source: %s\n", original_source);
182
183 // Hardcode the variable
184 STRING *var = tc->variable ? string_strdupz(tc->variable) : NULL;
185 expression_hardcode_variable(exp, var, tc->hardcode_value);
186 if (var) string_freez(var);
187
188 // Get the modified source
189 const char *modified_source = expression_source(exp);
190 printf(" Modified source: %s\n", modified_source);
191
192 // Check if source was modified as expected
193 bool source_correct = true;
194 if (tc->expected_source &&
195 strcmp(modified_source, tc->expected_source) != 0) {
196 printf(" FAILED: Source doesn't match expected.\n");
197 printf(" Expected: %s\n", tc->expected_source);
198 printf(" Actual: %s\n", modified_source);
199 source_correct = false;
200 }
201
202 // Evaluate the expression
203 expression_evaluate(exp);
204
205 // Check error code
206 bool error_correct = (exp->error == tc->expected_error);
207 if (!error_correct) {
208 printf(" FAILED: Error code doesn't match expected.\n");
209 printf(" Expected error: %d (%s)\n",
210 tc->expected_error, expression_strerror(tc->expected_error));
211 printf(" Actual error: %d (%s)\n",
212 exp->error, expression_strerror(exp->error));
213 }
214
215 // Check result if we expected no error
216 bool result_correct = true;
217 if (tc->expected_error == EVAL_ERROR_OK) {
218 NETDATA_DOUBLE result = expression_result(exp);
219 printf(" Result: %f\n", result);
220
221 if (fabs(result - tc->expected_result) > 0.000001) {
222 printf(" FAILED: Result doesn't match expected.\n");
223 printf(" Expected: %f\n", tc->expected_result);
224 printf(" Actual: %f\n", result);
225 result_correct = false;
226 }
227 }
228
229 // Determine if test passed overall
230 if (source_correct && error_correct && result_correct) {
231 printf(" PASSED\n");
232 passed++;
233 } else {
234 failed++;
235 }
236
237 // Clean up
238 expression_free(exp);
239 }
240
241 // Report results
242 printf("\nHardcode variable test results: %d passed, %d failed\n", passed, failed);
243 return failed;
244 }