From: Josef 'Jeff' Sipek Date: Tue, 19 Jan 2021 18:46:13 +0000 (-0500) Subject: lib: event-filter - Implement filter boolean expression unit tests X-Git-Tag: 2.3.14.rc1~88 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=01ccf687d660331ca3e483c90582156757cebb7f;p=thirdparty%2Fdovecot%2Fcore.git lib: event-filter - Implement filter boolean expression unit tests This is a exhaustive test of the boolean operators using field equality comparisons. This test code focuses on the boolean operators (AND, OR, NOT) and not on the comparison operators (=, <, >, <=, and >=) and their arguments (event names, source locations, categories). --- diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index f2cc13e745..645b4336aa 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -379,6 +379,7 @@ test_lib_SOURCES = \ test-data-stack.c \ test-event-category-register.c \ test-event-filter.c \ + test-event-filter-expr.c \ test-event-filter-parser.c \ test-event-flatten.c \ test-event-log.c \ diff --git a/src/lib/test-event-filter-expr.c b/src/lib/test-event-filter-expr.c new file mode 100644 index 0000000000..12fb77a941 --- /dev/null +++ b/src/lib/test-event-filter-expr.c @@ -0,0 +1,243 @@ +/* Copyright (c) 2021 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str.h" +#include "strescape.h" +#include "event-filter.h" +#include "event-filter-private.h" + +#define STRING1 "X" +#define STRING2 "Y" + +/* dummy values, at least for now */ +#define SOURCE_FILENAME "blah.c" +#define SOURCE_LINE 123 + +static void check_expr(struct event *event, + struct event_filter *filter, + enum event_filter_log_type log_type, + bool expected) +{ + struct event_filter_node *expr; + unsigned int num_queries; + bool got; + + /* get at the expr inside the filter */ + expr = event_filter_get_expr_for_testing(filter, &num_queries); + test_assert(num_queries == 1); /* should have only one query */ + + got = event_filter_query_match_eval(expr, event, + SOURCE_FILENAME, SOURCE_LINE, + log_type); + test_assert(got == expected); +} + +static void do_test_expr(const char *filter_string, struct event *event, + enum event_filter_log_type log_type, + bool expected) +{ + const char *error; + + test_begin(t_strdup_printf("%.*s log type + event {a=%s, b=%s} + filter '%s' (exp %s)", + 3, /* truncate the type name to avoid CI seeing 'warning' messages */ + event_filter_category_from_log_type(log_type), + event_find_field_str(event, "a"), + event_find_field_str(event, "b"), + filter_string, + expected ? "true" : "false")); + + /* set up the filter expression */ + struct event_filter *filter = event_filter_create(); + test_assert(event_filter_parse(filter_string, filter, &error) == 0); + + check_expr(event, filter, log_type, expected); + + test_end(); +} + +static void test_unary_expr(struct event *event, + const char *expr, bool truth, + enum event_filter_log_type log_type) +{ + /* + * The UNARY() macro checks: + * + * 1. expr + * 2. NOT expr + * 3. NOT (expr) + * + * Note that numbers 2 and 3 are equivalent. + * + * The truth argument specifies the expected truth-iness of the + * passed in expression. + */ +#define UNARY() \ + T_BEGIN { \ + do_test_expr(expr, \ + event, log_type, truth); \ + do_test_expr(t_strdup_printf("NOT %s", expr), \ + event, log_type, !truth); \ + do_test_expr(t_strdup_printf("NOT (%s)", expr), \ + event, log_type, !truth); \ + } T_END + + UNARY(); +} + +static void test_binary_expr(struct event *event, + const char *expr1, const char *expr2, + bool truth1, bool truth2, + enum event_filter_log_type log_type) +{ + /* + * The BINARY() macro checks: + * + * 1. expr1 op expr2 + * 2. NOT expr1 op expr2 + * 3. NOT (expr1) op expr2 + * 4. (NOT expr1) op expr2 + * 5. expr1 op NOT expr2 + * 6. expr1 op NOT (expr2) + * 7. expr1 op (NOT expr2) + * 8. NOT (expr1 op expr2) + * 9. NOT expr1 op NOT expr2 + * 10. NOT (expr1) op NOT (expr2) + * 11. (NOT expr1) op (NOT expr2) + * + * Where op is OR or AND. + * + * Note that: + * - numbers 2, 3, and 4 are equivalent + * - numbers 5, 6, and 7 are equivalent + * - numbers 9, 10, and 11 are equivalent + * + * The truth arugments specify the expected truth-iness of the + * passed in expressions. + */ +#define BINARY(opstr, op) \ + T_BEGIN { \ + do_test_expr(t_strdup_printf("%s %s %s", expr1, opstr, expr2),\ + event, log_type, \ + (truth1) op (truth2)); \ + do_test_expr(t_strdup_printf("NOT %s %s %s", expr1, opstr, expr2),\ + event, log_type, \ + !(truth1) op (truth2)); \ + do_test_expr(t_strdup_printf("NOT (%s) %s %s", expr1, opstr, expr2),\ + event, log_type, \ + !(truth1) op (truth2)); \ + do_test_expr(t_strdup_printf("(NOT %s) %s %s", expr1, opstr, expr2),\ + event, log_type, \ + !(truth1) op (truth2)); \ + do_test_expr(t_strdup_printf("%s %s NOT %s", expr1, opstr, expr2),\ + event, log_type, \ + (truth1) op !(truth2)); \ + do_test_expr(t_strdup_printf("%s %s NOT (%s)", expr1, opstr, expr2),\ + event, log_type, \ + (truth1) op !(truth2)); \ + do_test_expr(t_strdup_printf("%s %s (NOT %s)", expr1, opstr, expr2),\ + event, log_type, \ + (truth1) op !(truth2)); \ + do_test_expr(t_strdup_printf("NOT (%s %s %s)", expr1, opstr, expr2),\ + event, log_type, \ + !((truth1) op (truth2))); \ + do_test_expr(t_strdup_printf("NOT %s %s NOT %s", expr1, opstr, expr2),\ + event, log_type, \ + !(truth1) op !(truth2)); \ + do_test_expr(t_strdup_printf("NOT (%s) %s NOT (%s)", expr1, opstr, expr2),\ + event, log_type, \ + !(truth1) op !(truth2)); \ + do_test_expr(t_strdup_printf("(NOT %s) %s (NOT %s)", expr1, opstr, expr2),\ + event, log_type, \ + !(truth1) op !(truth2)); \ + } T_END + + BINARY("OR", ||); + BINARY("AND", &&); +} + +static void test_event_filter_expr_fields(enum event_filter_log_type log_type) +{ + static const char *values[] = { + NULL, + "", + STRING1, + STRING2, + }; + unsigned int a, b; + +#define STR_IS_EMPTY(v) \ + (((v) == NULL) || (strcmp("", (v)) == 0)) +#define STR_MATCHES(v, c) \ + (((v) != NULL) && (strcmp((c), (v)) == 0)) + + /* unary */ + for (a = 0; a < N_ELEMENTS(values); a++) { + /* set up the event to match against */ + struct event *event = event_create(NULL); + event_add_str(event, "a", values[a]); + + test_unary_expr(event, + "a=\"\"", + STR_IS_EMPTY(values[a]), + log_type); + test_unary_expr(event, + "a=" STRING1, + STR_MATCHES(values[a], STRING1), + log_type); + + event_unref(&event); + } + + /* binary */ + for (a = 0; a < N_ELEMENTS(values); a++) { + for (b = 0; b < N_ELEMENTS(values); b++) { + /* set up the event to match against */ + struct event *event = event_create(NULL); + event_add_str(event, "a", values[a]); + event_add_str(event, "b", values[b]); + + test_binary_expr(event, + "a=\"\"", + "b=\"\"", + STR_IS_EMPTY(values[a]), + STR_IS_EMPTY(values[b]), + log_type); + test_binary_expr(event, + "a=" STRING1, + "b=\"\"", + STR_MATCHES(values[a], STRING1), + STR_IS_EMPTY(values[b]), + log_type); + test_binary_expr(event, + "a=\"\"", + "b=" STRING2, + STR_IS_EMPTY(values[a]), + STR_MATCHES(values[b], STRING2), + log_type); + test_binary_expr(event, + "a=" STRING1, + "b=" STRING2, + STR_MATCHES(values[a], STRING1), + STR_MATCHES(values[b], STRING2), + log_type); + + event_unref(&event); + } + } +} + +void test_event_filter_expr(void) +{ + static const enum event_filter_log_type log_types[] = { + EVENT_FILTER_LOG_TYPE_DEBUG, + EVENT_FILTER_LOG_TYPE_INFO, + EVENT_FILTER_LOG_TYPE_WARNING, + EVENT_FILTER_LOG_TYPE_ERROR, + EVENT_FILTER_LOG_TYPE_FATAL, + EVENT_FILTER_LOG_TYPE_PANIC, + }; + unsigned int i; + + for (i = 0; i < N_ELEMENTS(log_types); i++) + test_event_filter_expr_fields(log_types[i]); +} diff --git a/src/lib/test-lib.inc b/src/lib/test-lib.inc index b9bbac617e..db959561f7 100644 --- a/src/lib/test-lib.inc +++ b/src/lib/test-lib.inc @@ -21,6 +21,7 @@ FATAL(fatal_data_stack) TEST(test_event_category_register) FATAL(fatal_event_category_register) TEST(test_event_filter) +TEST(test_event_filter_expr) TEST(test_event_filter_parser) TEST(test_event_flatten) TEST(test_event_log)