]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: event-filter - Implement filter boolean expression unit tests
authorJosef 'Jeff' Sipek <jeff.sipek@open-xchange.com>
Tue, 19 Jan 2021 18:46:13 +0000 (13:46 -0500)
committerJosef 'Jeff' Sipek <jeff.sipek@open-xchange.com>
Tue, 19 Jan 2021 18:46:13 +0000 (13:46 -0500)
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).

src/lib/Makefile.am
src/lib/test-event-filter-expr.c [new file with mode: 0644]
src/lib/test-lib.inc

index f2cc13e745a36dacc04e5f04e40e24afc4cc298e..645b4336aa3525dc8e33ed5b9067661f745ca6d1 100644 (file)
@@ -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 (file)
index 0000000..12fb77a
--- /dev/null
@@ -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]);
+}
index b9bbac617ec2267ecb8e5a953c9a26bb5bcc0885..db959561f75701ba6bfa1e4246bc742625947612 100644 (file)
@@ -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)