libamxp  1.4.0
Patterns C Implementation
test_expression.c
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** SPDX-License-Identifier: BSD-2-Clause-Patent
4 **
5 ** SPDX-FileCopyrightText: Copyright (c) 2023 SoftAtHome
6 **
7 ** Redistribution and use in source and binary forms, with or without modification,
8 ** are permitted provided that the following conditions are met:
9 **
10 ** 1. Redistributions of source code must retain the above copyright notice,
11 ** this list of conditions and the following disclaimer.
12 **
13 ** 2. Redistributions in binary form must reproduce the above copyright notice,
14 ** this list of conditions and the following disclaimer in the documentation
15 ** and/or other materials provided with the distribution.
16 **
17 ** Subject to the terms and conditions of this license, each copyright holder
18 ** and contributor hereby grants to those receiving rights under this license
19 ** a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
20 ** (except for failure to satisfy the conditions of this license) patent license
21 ** to make, have made, use, offer to sell, sell, import, and otherwise transfer
22 ** this software, where such license applies only to those patent claims, already
23 ** acquired or hereafter acquired, licensable by such copyright holder or contributor
24 ** that are necessarily infringed by:
25 **
26 ** (a) their Contribution(s) (the licensed copyrights of copyright holders and
27 ** non-copyrightable additions of contributors, in source or binary form) alone;
28 ** or
29 **
30 ** (b) combination of their Contribution(s) with the work of authorship to which
31 ** such Contribution(s) was added by such copyright holder or contributor, if,
32 ** at the time the Contribution is added, such addition causes such combination
33 ** to be necessarily infringed. The patent license shall not apply to any other
34 ** combinations which include the Contribution.
35 **
36 ** Except as expressly stated above, no rights or licenses from any copyright
37 ** holder or contributor is granted under this license, whether expressly, by
38 ** implication, estoppel or otherwise.
39 **
40 ** DISCLAIMER
41 **
42 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
43 ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45 ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
46 ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
48 ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
49 ** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
50 ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
51 ** USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52 **
53 ****************************************************************************/
54 #include <sys/signalfd.h>
55 #include <signal.h>
56 #include <unistd.h>
57 #include <stdlib.h>
58 #include <stdio.h>
59 #include <string.h>
60 
61 #include <stdarg.h>
62 #include <stddef.h>
63 #include <setjmp.h>
64 #include <fcntl.h>
65 #include <cmocka.h>
66 
67 #include <amxc/amxc.h>
68 #include <amxp/amxp_expression.h>
69 
70 #include "test_expression.h"
71 
72 typedef struct _evaluator {
73  bool result;
74  char text[512];
76 
77 #include <amxc/amxc_macros.h>
78 
79 void test_can_create_expression(UNUSED void** state) {
80  amxp_expr_t expression;
81  amxp_expr_t* expr = NULL;
82 
83  assert_int_equal(amxp_expr_init(&expression, "true"), 0);
84  assert_string_equal(expression.expression, "true");
85  amxp_expr_clean(&expression);
86  assert_ptr_equal(expression.expression, NULL);
87 
88  assert_int_equal(amxp_expr_new(&expr, "true"), 0);
89  assert_string_equal(expr->expression, "true");
90  amxp_expr_clean(expr);
91  assert_ptr_equal(expr->expression, NULL);
92  amxp_expr_delete(&expr);
93  assert_ptr_equal(expr, NULL);
94 }
95 
97  amxp_expr_t expression;
98  amxp_expr_status_t status = 0;
99 
100  eval_t evals[] = {
101  { true, "true" },
102  { false, "false"},
103  { true, "True"},
104  { false, "False"},
105  { true, "TrUe"},
106  { false, "FaLsE"},
107  { false, "true && false" },
108  { true, "true || false" },
109  { true, "" },
110  { true, "True || False" },
111  { false, "True && False" }
112  };
113 
114  for(uint32_t i = 0; i < sizeof(evals) / sizeof(evals[0]); i++) {
115  printf("Evaluating \"%s\"\n", evals[i].text);
116  assert_int_equal(amxp_expr_init(&expression, evals[i].text), 0);
117  assert_true(amxp_expr_eval(&expression, &status) == evals[i].result);
118  assert_int_equal(status, 0);
119  amxp_expr_clean(&expression);
120  }
121 }
122 
124  amxp_expr_t expression;
125  amxp_expr_status_t status = 0;
126 
127  eval_t evals[] = {
128  { true, "true || false && true" },
129  { true, "(true || false) && true"},
130  { false, "false || true && false" },
131  { false, "(false || true) && false" },
132  { false, "false || false && false" },
133  { false, "true && false || true && false"},
134  { false, "false || false && true || false"},
135  { true, "!(false || false) && true || false"},
136  { true, "!false && true"},
137  { true, "!false || false"},
138  { true, "!false || true"},
139  { false, "!(false || true)"},
140  { true, "true || false && false || false" },
141  { false, "(true || false) && (false || false)" },
142  };
143 
144  for(uint32_t i = 0; i < sizeof(evals) / sizeof(evals[0]); i++) {
145  printf("Evaluating \"%s\"\n", evals[i].text);
146  assert_int_equal(amxp_expr_init(&expression, evals[i].text), 0);
147  assert_true(amxp_expr_eval(&expression, &status) == evals[i].result);
148  assert_int_equal(status, 0);
149  amxp_expr_dump_tree(&expression);
150  amxp_expr_clean(&expression);
151  }
152 }
153 
155  amxp_expr_t expression;
156  amxp_expr_status_t status = 0;
157 
158  eval_t evals[] = {
159  { true, "10 > 0" },
160  { false, "10 > 10" },
161  { false, "10 < 0"},
162  { false, "10 < 10"},
163  { true, "10 >= 10" },
164  { true, "10 <= 10" },
165  { true, "\"bagpipe\" >= \"b\"" },
166  { true, "\"bagpipe\" >= \"bagpipe\""},
167  { false, "\"bagpipe\" <= \"aardvark\"" },
168  { true, "\"bagpipe\" >= \"aardvark\""},
169  { true, "\"This is some tekst\" != \"Diferent & Also, Text\""},
170  { true, "'Text' in ['bagpipe', 'aardvark', 'Text', 10]"},
171  { false, "'rabbit' in ['bagpipe', 'aardvark', 'Text', 10]"},
172  { true, "'the rabbit went into the rabbit hole' starts with 'the rabbit'"},
173  { false, "'Alice went into the rabbit hole' starts with 'the rabbit'"},
174  { true, "'the rabbit went into the rabbit hole' starts\t with 'the rabbit'"},
175  { true, "'rabbit' in 'the rabbit went into the rabbit hole'"},
176  { true, "'hole' in 'the rabbit went into the rabbit hole'"},
177  { false, "'Alice' in 'the rabbit went into the rabbit hole'"},
178  { false, "'Alice' in 'Al'"},
179  { true, "'Alice' starts with ['Al', 'Ra']" },
180  { true, "'Rabbit' starts with ['Al', 'Ra']" },
181  { true, "is_empty([])"},
182  { false, "is_empty([1,2])"},
183  { true, "'the rabbit went into the rabbit hole' ends with 'rabbit hole'"},
184  { false, "'the rabbit went into the rabbit hole' ends with 'rabbit pit'"},
185  { false, "'the rabbit' ends with 'very long string that does not crash'"},
186  { true, "'Rabbit' ends with ['ce', 'it']" }
187  };
188 
189  for(uint32_t i = 0; i < sizeof(evals) / sizeof(evals[0]); i++) {
190  printf("Evaluating \"%s\"\n", evals[i].text);
191  fflush(stdout);
192  assert_int_equal(amxp_expr_init(&expression, evals[i].text), 0);
193  assert_true(amxp_expr_eval(&expression, &status) == evals[i].result);
194  assert_int_equal(status, 0);
195  amxp_expr_dump_tree(&expression);
196  amxp_expr_clean(&expression);
197  }
198 }
199 
200 void test_can_fetch_fields(UNUSED void** state) {
201  amxp_expr_t expression;
202  amxc_var_t data;
203  amxc_var_t* subtable;
204  amxc_var_t* subarray;
205  amxc_ts_t now;
206  amxp_expr_status_t status = 0;
207 
208  eval_t evals[] = {
209  { true, "'brown' in CSVText"},
210  { true, "TextField == \"This is text\"" },
211  { true, "MyTable.Text == \"Hello World\"" },
212  { true, ".MyTable.Number == 100" },
213  { true, "MyTable.Boolean" },
214  { true, "MyArray.0 == \"Item1\""},
215  { true, ".MyArray.1 == \"-20000\""},
216  { true, "MyArray.2"},
217  { true, "MyArray.3 > \"1970-01-01T00:00:00Z\""},
218  { true, "\"This is text\" == TextField" },
219  { true, "\"Hello World\" == MyTable.Text;" },
220  { true, "100 == MyTable.Number" },
221  { true, "\"Item1\" == MyArray.0"},
222  { true, "\"-20000\" == MyArray.1;"},
223  { true, "\"1970-01-01T00:00:00Z\" < MyArray.3"},
224  { false, "MyTable.Number > MyArray.1" },
225  { true, "MyArray.1 < MyTable.Number" },
226  { true, "TextField matches \"This.*\""},
227  { false, "TextField matches \"Not This.*\""},
228  { true, "TextField starts with 'This'"},
229  { false, "TextField starts with 'But Not This'"},
230  { true, "'fox' in CSVText"},
231  { false, "'red' in CSVText"}
232  };
233 
234  amxc_var_init(&data);
235  amxc_var_set_type(&data, AMXC_VAR_ID_HTABLE);
236  amxc_ts_now(&now);
237 
238  amxc_var_add_key(cstring_t, &data, "TextField", "This is text");
239  subtable = amxc_var_add_key(amxc_htable_t, &data, "MyTable", NULL);
240  amxc_var_add_key(cstring_t, subtable, "Text", "Hello World");
241  amxc_var_add_key(uint32_t, subtable, "Number", 100);
242  amxc_var_add_key(bool, subtable, "Boolean", true);
243  subarray = amxc_var_add_key(amxc_llist_t, &data, "MyArray", NULL);
244  amxc_var_add(cstring_t, subarray, "Item1");
245  amxc_var_add(int64_t, subarray, -20000);
246  amxc_var_add(bool, subarray, true);
247  amxc_var_add(amxc_ts_t, subarray, &now);
248  amxc_var_add_key(csv_string_t, &data, "CSVText", "The,big,brown,fox,jumped");
249 
250  printf("Test data = ");
251  fflush(stdout);
252  amxc_var_dump(&data, STDOUT_FILENO);
253 
254  for(uint32_t i = 0; i < sizeof(evals) / sizeof(evals[0]); i++) {
255  bool result = false;
256  printf("Evaluating \"%s\" (expecting %s - ", evals[i].text, evals[i].result ? "true" : "false");
257  fflush(stdout);
258  assert_int_equal(amxp_expr_init(&expression, evals[i].text), 0);
259  amxp_expr_dump_tree(&expression);
260  result = amxp_expr_eval_var(&expression, &data, &status);
261  assert_int_equal(status, 0);
262  printf("got %s)\n", result ? "true" : "false");
263  fflush(stdout);
264  assert_true(result == evals[i].result);
265  amxp_expr_clean(&expression);
266  }
267 
268  subarray = GETP_ARG(&data, "CSVText");
269  assert_int_equal(amxc_var_type_of(subarray), AMXC_VAR_ID_CSV_STRING);
270 
271  amxc_var_clean(&data);
272 }
273 
274 void test_invalid_field_names(UNUSED void** state) {
275  amxp_expr_t expression;
276  amxc_var_t data;
277  amxc_var_t* subtable;
278  amxc_var_t* subarray;
279  amxc_ts_t now;
280  amxp_expr_status_t status = 0;
281 
282  eval_t evals[] = {
283  { false, "MyArray.NotExisting == \"Item1\""},
284  { false, "MyTable.99"},
285  { false, "\"TEXT\" matches Not.Existing.Field.0"},
286  };
287 
288  amxc_var_init(&data);
289  amxc_var_set_type(&data, AMXC_VAR_ID_HTABLE);
290  amxc_ts_now(&now);
291 
292  amxc_var_add_key(cstring_t, &data, "TextField", "This is text");
293  subtable = amxc_var_add_key(amxc_htable_t, &data, "MyTable", NULL);
294  amxc_var_add_key(cstring_t, subtable, "Text", "Hello World");
295  amxc_var_add_key(uint32_t, subtable, "Number", 100);
296  amxc_var_add_key(bool, subtable, "Boolean", true);
297  subarray = amxc_var_add_key(amxc_llist_t, &data, "MyArray", NULL);
298  amxc_var_add(cstring_t, subarray, "Item1");
299  amxc_var_add(int64_t, subarray, -20000);
300  amxc_var_add(bool, subarray, true);
301  amxc_var_add(amxc_ts_t, subarray, &now);
302  amxc_var_dump(&data, STDOUT_FILENO);
303 
304  for(uint32_t i = 0; i < sizeof(evals) / sizeof(evals[0]); i++) {
305  printf("Evaluating \"%s\"\n", evals[i].text);
306  assert_int_equal(amxp_expr_init(&expression, evals[i].text), 0);
307  assert_false(amxp_expr_eval_var(&expression, &data, &status));
308  assert_int_not_equal(status, 0);
309  amxp_expr_clean(&expression);
310  fflush(stdout);
311  }
312 
313  amxc_var_clean(&data);
314 }
315 
316 void test_invalid_syntax(UNUSED void** state) {
317  amxp_expr_t expression;
318  amxp_expr_t* expr = NULL;
319  eval_t evals[] = {
320  { false, "Hello <> Goobey"},
321  { false, "\"Text\"Field @ Hello World"},
322  { false, "100"},
323  { false, "['a', 'b', 'c'] in 'abc'"},
324  { false, "'Alice' in 1024"}
325  };
326 
327  for(uint32_t i = 0; i < sizeof(evals) / sizeof(evals[0]); i++) {
328  printf("Evaluating \"%s\"\n", evals[i].text);
329  assert_int_not_equal(amxp_expr_init(&expression, evals[i].text), 0);
330  fflush(stdout);
331  }
332 
333  for(uint32_t i = 0; i < sizeof(evals) / sizeof(evals[0]); i++) {
334  assert_int_not_equal(amxp_expr_new(&expr, evals[i].text), 0);
335  assert_ptr_equal(expr, NULL);
336  fflush(stdout);
337  }
338 }
339 
340 void test_invalid_value_types(UNUSED void** state) {
341  amxp_expr_t expression;
342  amxp_expr_t* expr = NULL;
343 
344  eval_t evals[] = {
345  { false, "'Some text' == ['Some', 'text']"},
346  { false, "[1,2,3,4] > 10"},
347  { false, "[1,2,3,4] matches ['Part1', 'Part2']"}
348  };
349 
350  for(uint32_t i = 0; i < sizeof(evals) / sizeof(evals[0]); i++) {
351  assert_int_equal(amxp_expr_init(&expression, evals[i].text), amxp_expr_status_invalid_value);
352  fflush(stdout);
353  }
354 
355  for(uint32_t i = 0; i < sizeof(evals) / sizeof(evals[0]); i++) {
356  assert_int_equal(amxp_expr_new(&expr, evals[i].text), amxp_expr_status_invalid_value);
357  assert_ptr_equal(expr, NULL);
358  fflush(stdout);
359  }
360 }
361 
363  amxc_var_t* value,
364  const char* path,
365  void* priv) {
367  amxc_var_t* data = (amxc_var_t*) priv;
368  amxc_var_t* field = amxc_var_get_path(data, path, AMXC_VAR_FLAG_DEFAULT);
369  if(amxc_var_is_null(field)) {
370  if(strcmp(path, "available") == 0) {
371  amxc_var_set(bool, value, true);
372  } else {
373  amxc_var_set(bool, value, false);
374  }
375  retval = amxp_expr_status_ok;
376  } else {
377  if(amxc_var_copy(value, field) == 0) {
378  retval = amxp_expr_status_ok;
379  }
380  }
381 
382  return retval;
383 }
384 
386  amxp_expr_t expression;
387  amxc_var_t data;
388  amxc_var_t* subtable;
389  amxc_var_t* subarray;
390  amxc_ts_t now;
391  amxp_expr_status_t status = 0;
392 
393  eval_t evals[] = {
394  { true, "available"},
395  { false, "NOT_available"},
396  { true, ".MyTable.Number > 100 || available"},
397  { true, "MyTable.Number > 100 || available"},
398  { false, ".MyTable.Number > 100 || !available"},
399  { false, "MyTable.Number > 100 || !available"},
400  { false, "{MyTable.Number} > 100 || not_available"}
401  };
402 
403  amxc_var_init(&data);
404  amxc_var_set_type(&data, AMXC_VAR_ID_HTABLE);
405  amxc_ts_now(&now);
406 
407  amxc_var_add_key(cstring_t, &data, "TextField", "This is text");
408  subtable = amxc_var_add_key(amxc_htable_t, &data, "MyTable", NULL);
409  amxc_var_add_key(cstring_t, subtable, "Text", "Hello World");
410  amxc_var_add_key(uint32_t, subtable, "Number", 100);
411  amxc_var_add_key(bool, subtable, "Boolean", true);
412  subarray = amxc_var_add_key(amxc_llist_t, &data, "MyArray", NULL);
413  amxc_var_add(cstring_t, subarray, "Item1");
414  amxc_var_add(int64_t, subarray, -20000);
415  amxc_var_add(bool, subarray, true);
416  amxc_var_add(amxc_ts_t, subarray, &now);
417 
418  for(uint32_t i = 0; i < sizeof(evals) / sizeof(evals[0]); i++) {
419  printf("Evaluating \"%s\"\n", evals[i].text);
420  assert_int_equal(amxp_expr_init(&expression, evals[i].text), 0);
421  assert_true(amxp_expr_evaluate(&expression, custom_field_fetcher, &data, &status) == evals[i].result);
422  assert_int_equal(status, 0);
423  amxp_expr_clean(&expression);
424  fflush(stdout);
425  }
426 
427  amxc_var_clean(&data);
428 }
429 
431  amxp_expr_t* expr = NULL;
432  amxp_expr_t expression;
433 
434  assert_int_not_equal(amxp_expr_new(NULL, ""), 0);
435  assert_int_not_equal(amxp_expr_new(&expr, NULL), 0);
436  assert_ptr_equal(expr, NULL);
437 
438  assert_int_equal(amxp_expr_new(&expr, "10 > 20"), 0);
439  assert_ptr_not_equal(expr, NULL);
440 
441  amxp_expr_delete(&expr);
442  assert_ptr_equal(expr, NULL);
443  amxp_expr_delete(&expr);
444  amxp_expr_delete(NULL);
445 
446  assert_int_not_equal(amxp_expr_init(NULL, "20 < 10"), 0);
447  assert_int_not_equal(amxp_expr_init(&expression, NULL), 0);
448  amxp_expr_clean(NULL);
449 }
450 
452  amxp_expr_t expression;
453  amxc_var_t data;
454  amxc_var_t* subtable;
455  amxc_var_t* subarray;
456  amxc_ts_t now;
457  amxp_expr_status_t status = 0;
458 
459  eval_t evals[] = {
460  { false, "first_existing(MyTable.Text, .MyArray.0) == \"Item1\""},
461  { true, "first_existing(MyTable.Text, MyArray.0) == \"Hello World\""},
462  { true, "first_existing(.MyTable.NotExistingText, MyArray.0) == \"Item1\""},
463  { true, "first_existing(.MyTable.NotExistingText, MyArray.99, FakeItem.0.NotExisting, 'Item1') == \"Item1\""},
464  { false, "first_existing(.MyTable.NotExistingText, MyArray.99, FakeItem.0.NotExisting, MyTable.Text) == \"Item1\""},
465  };
466 
467  amxc_var_init(&data);
468  amxc_var_set_type(&data, AMXC_VAR_ID_HTABLE);
469  amxc_ts_now(&now);
470 
471  amxc_var_add_key(cstring_t, &data, "TextField", "This is text");
472  subtable = amxc_var_add_key(amxc_htable_t, &data, "MyTable", NULL);
473  amxc_var_add_key(cstring_t, subtable, "Text", "Hello World");
474  amxc_var_add_key(uint32_t, subtable, "Number", 100);
475  amxc_var_add_key(bool, subtable, "Boolean", true);
476  subarray = amxc_var_add_key(amxc_llist_t, &data, "MyArray", NULL);
477  amxc_var_add(cstring_t, subarray, "Item1");
478  amxc_var_add(int64_t, subarray, -20000);
479  amxc_var_add(bool, subarray, true);
480  amxc_var_add(amxc_ts_t, subarray, &now);
481  amxc_var_dump(&data, 1);
482 
483  for(uint32_t i = 0; i < sizeof(evals) / sizeof(evals[0]); i++) {
484  printf("Evaluating \"%s\"\n", evals[i].text);
485  assert_int_equal(amxp_expr_init(&expression, evals[i].text), 0);
486  assert_true(amxp_expr_eval_var(&expression, &data, &status) == evals[i].result);
487  assert_int_equal(status, 0);
488  amxp_expr_dump_tree(&expression);
489  amxp_expr_clean(&expression);
490  }
491 
492  amxc_var_clean(&data);
493 }
494 
496  amxp_expr_t expression;
497  amxc_var_t data;
498  amxc_var_t* subtable;
499  amxc_var_t* subarray;
500  amxc_ts_t now;
501 
502  amxc_var_init(&data);
503  amxc_var_set_type(&data, AMXC_VAR_ID_HTABLE);
504  amxc_ts_now(&now);
505 
506  amxc_var_add_key(cstring_t, &data, "TextField", "This is text");
507  subtable = amxc_var_add_key(amxc_htable_t, &data, "MyTable", NULL);
508  amxc_var_add_key(cstring_t, subtable, "Text", "Hello World");
509  amxc_var_add_key(uint32_t, subtable, "Number", 100);
510  amxc_var_add_key(bool, subtable, "Boolean", true);
511  subarray = amxc_var_add_key(amxc_llist_t, &data, "MyArray", NULL);
512  amxc_var_add(cstring_t, subarray, "Item1");
513  amxc_var_add(int64_t, subarray, -20000);
514  amxc_var_add(bool, subarray, true);
515  amxc_var_add(amxc_ts_t, subarray, &now);
516 
517  assert_int_not_equal(amxp_expr_init(&expression, "MyTable.Text matches \"[(adadasd.[\""), 0);
518 
519  amxc_var_clean(&data);
520 }
521 
522 void test_is_empty_function(UNUSED void** state) {
523  amxp_expr_t expression;
524  amxp_expr_status_t status = 0;
525 
526  eval_t evals[] = {
527  { true, "is_empty([])"},
528  { false, "is_empty([1,2])"}
529  };
530 
531  for(uint32_t i = 0; i < sizeof(evals) / sizeof(evals[0]); i++) {
532  printf("Evaluating \"%s\"\n", evals[i].text);
533  assert_int_equal(amxp_expr_init(&expression, evals[i].text), 0);
534  assert_true(amxp_expr_eval(&expression, &status) == evals[i].result);
535  assert_int_equal(status, 0);
536  amxp_expr_clean(&expression);
537  }
538 }
539 
541  amxp_expr_t expression;
542  amxp_expr_status_t status = 0;
543  amxc_var_t data;
544  amxc_var_t* subtable;
545  amxc_var_t* subarray;
546  amxc_ts_t now;
547 
548  eval_t evals[] = {
549  { false, "is_empty({MyTable})"},
550  { false, "is_empty({MyArray})"},
551  { true, "is_empty({EmptyArray})"},
552  { true, "is_empty({EmptyTable})"},
553  { true, "is_empty({MyTable.NotExisting})"},
554  { true, "is_empty({MyArray.99})"},
555  { false, "is_empty({})"},
556  { true, "is_empty()"},
557  };
558 
559  amxc_var_init(&data);
560  amxc_var_set_type(&data, AMXC_VAR_ID_HTABLE);
561  amxc_ts_now(&now);
562 
563  amxc_var_add_key(cstring_t, &data, "TextField", "This is text");
564  subtable = amxc_var_add_key(amxc_htable_t, &data, "MyTable", NULL);
565  amxc_var_add_key(cstring_t, subtable, "Text", "Hello World");
566  amxc_var_add_key(uint32_t, subtable, "Number", 100);
567  amxc_var_add_key(bool, subtable, "Boolean", true);
568  subarray = amxc_var_add_key(amxc_llist_t, &data, "MyArray", NULL);
569  amxc_var_add(cstring_t, subarray, "Item1");
570  amxc_var_add(int64_t, subarray, -20000);
571  amxc_var_add(bool, subarray, true);
572  amxc_var_add(amxc_ts_t, subarray, &now);
573  amxc_var_add_key(amxc_llist_t, &data, "EmptyArray", NULL);
574  amxc_var_add_key(amxc_htable_t, &data, "EmptyTable", NULL);
575 
576  for(uint32_t i = 0; i < sizeof(evals) / sizeof(evals[0]); i++) {
577  printf("Evaluating \"%s\"\n", evals[i].text);
578  assert_int_equal(amxp_expr_init(&expression, evals[i].text), 0);
579  assert_true(amxp_expr_eval_var(&expression, &data, &status) == evals[i].result);
580  assert_int_equal(status, 0);
581  amxp_expr_clean(&expression);
582  }
583 
584  amxc_var_clean(&data);
585 }
586 
587 void test_contains(UNUSED void** state) {
588  amxp_expr_t expression;
589  amxc_var_t data;
590  amxc_var_t* subtable;
591  amxc_var_t* subarray;
592  amxc_ts_t now;
593  amxp_expr_status_t status = 0;
594 
595  eval_t evals[] = {
596  { true, "contains('MyTable.Text')"},
597  { false, "contains('MyTable.Text.Extra')"},
598  { true, "contains('MyArray.0')"},
599  { false, "contains('MyArray.0.Extra')"},
600  { false, "contains('MyArray.99')"},
601  { true, "contains('Test', 'MyArray.0')"},
602  { false, "contains('MyArray.99', 'MyTable.Text.Extra', 'MyArray.0.Extra')"},
603  };
604 
605  amxc_var_init(&data);
606  amxc_var_set_type(&data, AMXC_VAR_ID_HTABLE);
607  amxc_ts_now(&now);
608 
609  amxc_var_add_key(cstring_t, &data, "TextField", "This is text");
610  subtable = amxc_var_add_key(amxc_htable_t, &data, "MyTable", NULL);
611  amxc_var_add_key(cstring_t, subtable, "Text", "Hello World");
612  amxc_var_add_key(uint32_t, subtable, "Number", 100);
613  amxc_var_add_key(bool, subtable, "Boolean", true);
614  subarray = amxc_var_add_key(amxc_llist_t, &data, "MyArray", NULL);
615  amxc_var_add(cstring_t, subarray, "Item1");
616  amxc_var_add(int64_t, subarray, -20000);
617  amxc_var_add(bool, subarray, true);
618  amxc_var_add(amxc_ts_t, subarray, &now);
619 
620  for(uint32_t i = 0; i < sizeof(evals) / sizeof(evals[0]); i++) {
621  printf("Evaluating \"%s\"\n", evals[i].text);
622  assert_int_equal(amxp_expr_init(&expression, evals[i].text), 0);
623  assert_true(amxp_expr_eval_var(&expression, &data, &status) == evals[i].result);
624  assert_int_equal(status, 0);
625  amxp_expr_clean(&expression);
626  }
627 
628  amxc_var_clean(&data);
629 }
630 
631 void test_contains_no_get_field(UNUSED void** state) {
632  amxp_expr_t expression;
633  amxp_expr_status_t status = 0;
634  amxc_var_t data;
635  amxc_var_t* subtable = NULL;
636 
637  eval_t evals[] = {
638  { false, "contains('MyTable.Text')"},
639  { false, "contains('MyTable.Text.Extra')"},
640  { false, "contains('MyArray.0')"},
641  { false, "contains('MyArray.0.Extra')"},
642  { false, "contains('MyArray.99')"},
643  };
644 
645  amxc_var_init(&data);
646  amxc_var_set_type(&data, AMXC_VAR_ID_HTABLE);
647  subtable = amxc_var_add_key(amxc_htable_t, &data, "MyTable", NULL);
648  amxc_var_add_key(cstring_t, subtable, "Text", "Hello World");
649 
650  for(uint32_t i = 0; i < sizeof(evals) / sizeof(evals[0]); i++) {
651  printf("Evaluating \"%s\"\n", evals[i].text);
652  assert_int_equal(amxp_expr_init(&expression, evals[i].text), 0);
653  assert_true(amxp_expr_evaluate(&expression, NULL, &data, &status) == evals[i].result);
654  assert_int_equal(status, 0);
655  amxp_expr_clean(&expression);
656  }
657 
658  amxc_var_clean(&data);
659 }
660 
661 void test_contains_no_data(UNUSED void** state) {
662  amxp_expr_t expression;
663  amxp_expr_status_t status = 0;
664 
665  eval_t evals[] = {
666  { false, "contains('MyTable.Text')"},
667  { false, "contains('MyTable.Text.Extra')"},
668  { false, "contains('MyArray.0')"},
669  { false, "contains('MyArray.0.Extra')"},
670  { false, "contains('MyArray.99')"},
671  };
672 
673  for(uint32_t i = 0; i < sizeof(evals) / sizeof(evals[0]); i++) {
674  printf("Evaluating \"%s\"\n", evals[i].text);
675  assert_int_equal(amxp_expr_init(&expression, evals[i].text), 0);
676  assert_true(amxp_expr_evaluate(&expression, NULL, NULL, &status) == evals[i].result);
677  assert_int_equal(status, 0);
678  amxp_expr_clean(&expression);
679  }
680 }
681 
683  amxp_expr_t expression;
684  amxp_expr_status_t status = 0;
685  amxc_var_t data;
686 
687  amxc_var_init(&data);
688  amxc_var_set_type(&data, AMXC_VAR_ID_HTABLE);
689 
690  assert_int_equal(amxp_expr_init(&expression, "contains()"), 0);
691  assert_false(amxp_expr_evaluate(&expression, NULL, &data, &status));
692  assert_int_equal(status, 0);
693  amxp_expr_clean(&expression);
694 
695  assert_int_equal(amxp_expr_init(&expression, "contains(100)"), 0);
696  assert_false(amxp_expr_evaluate(&expression, NULL, &data, &status));
697  assert_int_equal(status, 0);
698  amxp_expr_clean(&expression);
699 
700  amxc_var_clean(&data);
701 }
702 
703 void test_flag_expressions(UNUSED void** state) {
704  amxp_expr_t expression;
705  amxp_expr_status_t status = 0;
706  amxc_set_t set;
707 
708  amxc_set_init(&set, false);
709  amxc_set_parse(&set, "bridge ipv6 global lan interface");
710 
711  assert_int_equal(amxp_expr_init(&expression, "ipv4 || (ipv6 && global)"), 0);
712  assert_true(amxp_expr_eval_set(&expression, &set, &status));
713 
714  amxc_set_remove_flag(&set, "ipv6");
715 
716  assert_false(amxp_expr_eval_set(&expression, &set, &status));
717 
718  amxc_set_clean(&set);
719  amxp_expr_clean(&expression);
720 }
721 
723  amxp_expr_t expression;
724  amxp_expr_status_t status = 0;
725  amxc_set_t set;
726 
727  amxc_set_init(&set, false);
728  amxc_set_parse(&set, "bridge ipv6 global lan interface");
729 
730  assert_int_equal(amxp_expr_init(&expression, "ipv6 global"), 0);
731  assert_true(amxp_expr_eval_set(&expression, &set, &status));
732 
733  amxc_set_remove_flag(&set, "ipv6");
734 
735  assert_false(amxp_expr_eval_set(&expression, &set, &status));
736 
737  amxc_set_clean(&set);
738  amxp_expr_clean(&expression);
739 }
740 
741 void test_in_operators(UNUSED void** state) {
742  amxp_expr_t expression;
743  amxp_expr_status_t status = 0;
744  eval_t evals[] = {
745  { true, "'Some text' in ['Some text', 'other value']"},
746  { false, "'Some text' in ['some value', 'other value']"},
747  { true, "['Some text', 'other value'] ~= 'Some text'"},
748  { false, "['some value', 'other value'] ~= 'Some text'"},
749  };
750 
751  for(uint32_t i = 0; i < sizeof(evals) / sizeof(evals[0]); i++) {
752  printf("Evaluating \"%s\"\n", evals[i].text);
753  assert_int_equal(amxp_expr_init(&expression, evals[i].text), 0);
754  assert_true(amxp_expr_evaluate(&expression, NULL, NULL, &status) == evals[i].result);
755  assert_int_equal(status, 0);
756  amxp_expr_clean(&expression);
757  }
758 }
759 
760 void test_buildf_expression(UNUSED void** state) {
761  amxp_expr_t expression;
762  const char* expr_fmt = "'Some text' in ['%s', '%s']";
763 
764  printf("Building \"%s\"\n", expr_fmt);
765  assert_int_equal(amxp_expr_buildf(&expression, expr_fmt, "some value", "other value"), amxp_expr_status_ok);
766  amxp_expr_clean(&expression);
767 }
768 
770  amxp_expr_t* expression = NULL;
771  const char* expr_fmt = "'Some text' in ['%s', '%s']";
772 
773  printf("Building \"%s\"\n", expr_fmt);
774  assert_int_equal(amxp_expr_buildf_new(&expression, expr_fmt, "some value", "other value"), amxp_expr_status_ok);
775  amxp_expr_delete(&expression);
776 }
777 
779  amxp_expr_t expression;
780  const char* expr_fmt = "password == '%s'";
781 
782  printf("Building \"%s\"\n", expr_fmt);
783  assert_int_not_equal(amxp_expr_buildf(&expression, expr_fmt, "not password' or '' == '"), amxp_expr_status_ok);
784  amxp_expr_clean(&expression);
785 }
786 
788  amxp_expr_t expression;
789  const char* expr_fmt = "test == '%s'";
790 
791  printf("Building \"%s\"\n", expr_fmt);
792  assert_int_equal(amxp_expr_buildf(&expression, expr_fmt, "Whitelisted according to specs:"
793  "!#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{|}~",
794  "other value"), amxp_expr_status_ok);
795  amxp_expr_clean(&expression);
796 }
797 
799  amxp_expr_t expression;
800  const char* bad_expr_fmt = "test == '%s']";
801  const char* good_expr_fmt = "test == '%s'";
802 
803  assert_int_not_equal(amxp_expr_buildf(&expression, bad_expr_fmt, "test", "other value"), amxp_expr_status_ok);
804  assert_int_not_equal(amxp_expr_buildf(NULL, good_expr_fmt, "test"), amxp_expr_status_ok);
805  assert_int_not_equal(amxp_expr_buildf(&expression, NULL), amxp_expr_status_ok);
806 
807  amxp_expr_clean(&expression);
808 }
809 
810 static void s_testhelper_buildf(bool expect_accepted, const char* string) {
811  amxp_expr_t expression;
813  amxp_expr_status_t status_build = amxp_expr_buildf(&expression, "'%s' == '%s'", string, string);
814  bool matched = amxp_expr_eval(&expression, &status_eval);
815  if(expect_accepted) {
816  assert_true(matched);
817  assert_int_equal(status_build, amxp_expr_status_ok);
818  amxp_expr_clean(&expression);
819  } else {
820  assert_false(matched);
821  assert_int_not_equal(status_build, amxp_expr_status_ok);
822  }
823 }
824 
826  s_testhelper_buildf(true, "192.168.1.1");
827  s_testhelper_buildf(true, "AA:BB:CC:DD:EE:FF");
828  s_testhelper_buildf(true, "Hello world!");
829  s_testhelper_buildf(true, "Whitelisted according to specs: !#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{|}~");
830  s_testhelper_buildf(true, "");
831 
832  s_testhelper_buildf(false, "notpasword' or '' == '");
833  s_testhelper_buildf(false, "notpasword\" or \"\" == \"");
834  s_testhelper_buildf(false, "192.168.1.1\"); PasswordManager.MasterPassword=hacked; #");
835  s_testhelper_buildf(false, "192.168.1.1\\");
836  s_testhelper_buildf(false, "We ♥ UTF-8 but it is currently not whitelisted");
837 }
838 
839 void test_get_string(UNUSED void** state) {
840  const char* expr_string = NULL;
841 
842  // GIVEN an expression
843  amxp_expr_t expr;
844  amxp_expr_buildf(&expr, "username == '%s'", "Alice");
845 
846  // WHEN retrieiving its string representation
847  expr_string = amxp_expr_get_string(&expr);
848 
849  // THEN the string representation is returned
850  assert_string_equal("username == 'Alice'", expr_string);
851 
852  amxp_expr_clean(&expr);
853 }
854 
855 void test_get_string_null(UNUSED void** state) {
856  const char* expr_string = NULL;
857 
858  // GIVEN a NULL expression
859  amxp_expr_t* expr = NULL;
860 
861  // WHEN retrieving its string representation
862  expr_string = amxp_expr_get_string(expr);
863 
864  // THEN NULL is returned
865  assert_null(expr_string);
866 }
867 
869  // GIVEN a NULL expression
870  amxp_expr_t expr;
871  assert_int_not_equal(amxp_expr_init(&expr, "[[brackets"), 0);
872 
873  amxp_expr_clean(&expr);
874 }
875 
876 void test_equals_ignorecase(UNUSED void** state) {
877  amxc_var_t data;
878 
879  eval_t evals[] = {
880  // Case: Exactly the same
881  { true, "'HELLO world' ^= 'HELLO world'"},
882  // Case: upper/lowercase different
883  { true, "'HELLO world' ^= 'hello WORLD'"},
884  { true, "'HELLO WORLD' ^= 'hello world'"},
885  { true, "'hello world' ^= 'HELLO WORLD'"},
886  // Case: different characters
887  { false, "'hello world' ^= 'hallo world'"},
888  // Expression is substring
889  { false, "'abcd' ^= 'abc'"},
890  { false, "'abcd' ^= 'bcd'"},
891  { false, "'abcd' ^= 'bc'"},
892  { false, "'abcd' ^= ''"},
893  // Expression is superstring
894  { false, "'abc' ^= 'abcd'"},
895  { false, "'bcd' ^= 'abcd'"},
896  { false, "'bc' ^= 'abcd'"},
897  { false, "'' ^= 'abcd'"},
898  // Case: Special characters
899  { true, "'~♥!@#$%^&*(){}?+_SZVWwvzs-/=][' ^= '~♥!@#$%^&*(){}?+_SZVWwvzs-/=]['"},
900  { false, "'~♥!@#$%^&*(){}?+_SZVWwvzs-/=][' ^= '~♥!@#$%^&*(){}?+_SZVWwvzs-/=[]'"},
901  // Case: quote characters
902  { true, "'He said: \"Hello.\"' ^= 'He said: \"Hello.\"'"},
903  { true, "\"He said: 'Hello.'\" ^= \"He said: 'Hello.'\""},
904  { false, "\"He said: 'Hello.'\" ^= 'He said: \"Hello.\"'"},
905  // Case: Not a string
906  { true, "Number ^= '100'" },
907  { true, "Boolean ^= \"false\"" },
908  { true, "Boolean ^= \"FaLsE\"" },
909  { false, "Boolean ^= \"true\"" },
910  { true, "CSVText ^= \"the,BIG,brown,fox,jumped\""},
911  };
912 
913  amxc_var_init(&data);
914  amxc_var_set_type(&data, AMXC_VAR_ID_HTABLE);
915  amxc_var_add_key(uint32_t, &data, "Number", 100);
916  amxc_var_add_key(bool, &data, "Boolean", false);
917  amxc_var_add_key(csv_string_t, &data, "CSVText", "The,big,brown,fox,jumped");
918 
919  for(uint32_t i = 0; i < sizeof(evals) / sizeof(evals[0]); i++) {
920  amxp_expr_t expression;
922  printf("Evaluating \"%s\"\n", evals[i].text);
923  assert_int_equal(amxp_expr_init(&expression, evals[i].text), 0);
924  assert_int_equal(amxp_expr_eval_var(&expression, &data, &status), evals[i].result);
925  assert_int_equal(status, 0);
926  amxp_expr_clean(&expression);
927  }
928 
929  amxc_var_clean(&data);
930 }
931 
Ambiorix expression parser and evaluate API header file.
#define UNUSED
Definition: main.c:68
const char * amxp_expr_get_string(amxp_expr_t *expr)
Returns the string representation of the given expression.
amxp_expr_status_t amxp_expr_new(amxp_expr_t **expr, const char *expression)
Allocates and initializes an expression.
bool amxp_expr_eval(amxp_expr_t *expr, amxp_expr_status_t *status)
Evaluates an expression.
amxp_expr_status_t amxp_expr_buildf_new(amxp_expr_t **expr, const char *expr_fmt,...) __attribute__((format(printf
Allocates and initializes an expression with format-string and safety checks.
amxp_expr_status_t amxp_expr_buildf(amxp_expr_t *expr, const char *expr_fmt,...) __attribute__((format(printf
Initializes an expression structure.
bool amxp_expr_eval_set(amxp_expr_t *expr, const amxc_set_t *const data, amxp_expr_status_t *status)
Evaluates an expression against a set.
bool amxp_expr_eval_var(amxp_expr_t *expr, const amxc_var_t *const data, amxp_expr_status_t *status)
Evaluates an expression against a composite variant.
void amxp_expr_clean(amxp_expr_t *expr)
Clean-up the expression structure.
amxp_expr_status_t amxp_expr_init(amxp_expr_t *expr, const char *expression)
Initializes an expression structure.
bool amxp_expr_evaluate(amxp_expr_t *expr, amxp_expr_get_field_t fn, void *priv, amxp_expr_status_t *status)
Evaluates an expression.
void amxp_expr_dump_tree(amxp_expr_t *expr)
Dumps the binary tree in dot formatted text file to stdout.
enum _expr_status amxp_expr_status_t
Expression status/error codes.
void amxp_expr_delete(amxp_expr_t **expr)
Deletes a previously allocated expression structure.
@ amxp_expr_status_invalid_value
@ amxp_expr_status_unknown_error
@ amxp_expr_status_ok
char * expression
char text[512]
static void s_testhelper_buildf(bool expect_accepted, const char *string)
static amxp_expr_status_t custom_field_fetcher(UNUSED amxp_expr_t *expr, amxc_var_t *value, const char *path, void *priv)
void test_invalid_value_types(UNUSED void **state)
void test_api_arguments_validation(UNUSED void **state)
void test_buildf_expression_allocate(UNUSED void **state)
void test_buildf_are_strings_safe(UNUSED void **state)
void test_get_string_null(UNUSED void **state)
void test_in_operators(UNUSED void **state)
void test_expression_with_invalid_list_does_not_memory_leak(UNUSED void **state)
void test_can_fetch_fields(UNUSED void **state)
void test_get_string(UNUSED void **state)
void test_can_create_expression(UNUSED void **state)
void test_buildf_expression_invalid_args(UNUSED void **state)
void test_precedence_is_respected(UNUSED void **state)
void test_contains(UNUSED void **state)
void test_selects_first_in_first_existing(UNUSED void **state)
void test_contains_no_data(UNUSED void **state)
void test_invalid_field_names(UNUSED void **state)
struct _evaluator eval_t
void test_flag_expressions_no_operators(UNUSED void **state)
void test_invalid_syntax(UNUSED void **state)
void test_buildf_expression(UNUSED void **state)
void test_comperators_are_correct(UNUSED void **state)
void test_buildf_expression_all_whitelisted(UNUSED void **state)
void test_buildf_expression_invalid_value(UNUSED void **state)
void test_contains_invalid_usage(UNUSED void **state)
void test_is_empty_function(UNUSED void **state)
void test_can_evaluate_expression(UNUSED void **state)
void test_flag_expressions(UNUSED void **state)
void test_contains_no_get_field(UNUSED void **state)
void test_can_use_custom_field_fetcher(UNUSED void **state)
void test_fails_with_invalid_regexp(UNUSED void **state)
void test_is_empty_function_with_var(UNUSED void **state)
void test_equals_ignorecase(void **state)
static amxc_string_t path