]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: Add event_flatten() to return a flattened event
authorJosef 'Jeff' Sipek <jeff.sipek@open-xchange.com>
Fri, 15 Mar 2019 00:30:29 +0000 (20:30 -0400)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Mon, 20 May 2019 06:46:32 +0000 (06:46 +0000)
src/lib/Makefile.am
src/lib/lib-event.c
src/lib/lib-event.h
src/lib/test-event-flatten.c [new file with mode: 0644]
src/lib/test-lib.inc

index 856cc82539cbfd7aa7a0167e2b1b3c35057d53b1..f1fd95c5ff491dade8c14dbcb7c9e931fca7c633 100644 (file)
@@ -346,6 +346,7 @@ test_lib_SOURCES = \
        test-crc32.c \
        test-data-stack.c \
        test-event-filter.c \
+       test-event-flatten.c \
        test-event-log.c \
        test-failures.c \
        test-file-create-locked.c \
index 20b985db38a55ef6d1c0e3bd5deffac97df66d5a..a9ff06f075df9499bf4f4049b988419c71a76c8a 100644 (file)
@@ -122,6 +122,46 @@ struct event *event_dup(const struct event *source)
        return ret;
 }
 
+/*
+ * Copy the source's categories and fields recursively.
+ *
+ * We recurse to the parent before copying this event's data because we may
+ * be overriding a field.
+ */
+static void event_flatten_recurse(struct event *dst, struct event *src,
+                                 struct event *limit)
+{
+       if (src->parent != limit)
+               event_flatten_recurse(dst, src->parent, limit);
+
+       event_copy_categories(dst, src);
+       event_copy_fields(dst, src);
+}
+
+struct event *event_flatten(struct event *src)
+{
+       struct event *dst;
+
+       /* If we don't have a parent, we have nothing to flatten. */
+       if (src->parent == NULL)
+               return event_ref(src);
+
+       /* We have to flatten the event. */
+
+       dst = event_create(NULL);
+       dst = event_set_name(dst, src->sending_name);
+       dst = event_set_source(dst, src->source_filename, src->source_linenum,
+                              FALSE);
+
+       event_flatten_recurse(dst, src, NULL);
+
+       dst->tv_created_ioloop = src->tv_created_ioloop;
+       dst->tv_created = src->tv_created;
+       dst->tv_last_sent = src->tv_last_sent;
+
+       return dst;
+}
+
 #undef event_create
 struct event *event_create(struct event *parent, const char *source_filename,
                           unsigned int source_linenum)
index e598044572fd695f96537dd37531dab81f810fbd..63f7dbb0991085b9c5dd67affa242716f54aec80 100644 (file)
@@ -87,6 +87,11 @@ bool event_has_all_fields(struct event *event, const struct event *other);
 
 /* Returns the source event duplicated into a new event. */
 struct event *event_dup(const struct event *source);
+/* Returns a flattened version of the source event.
+   Both categories and fields will be flattened.
+   A new reference to the source event is returned if no flattening was
+   needed. */
+struct event *event_flatten(struct event *src);
 /* Copy all categories from source to dest.
    Only the categories in source event itself are copied.
    Parent events' categories aren't copied. */
diff --git a/src/lib/test-event-flatten.c b/src/lib/test-event-flatten.c
new file mode 100644 (file)
index 0000000..69673dd
--- /dev/null
@@ -0,0 +1,262 @@
+/* Copyright (c) 2019 Dovecot authors, see the included COPYING file */
+
+#include "test-lib.h"
+#include "ioloop.h"
+#include "time-util.h"
+#include "lib-event-private.h"
+#include "failures-private.h"
+
+#define CHECK_FLATTEN_SAME(e) \
+       check_event_same(event_flatten(e), (e))
+
+#define CHECK_FLATTEN_DIFF(e, c, nc, f, nf) \
+       check_event_diff(event_flatten(e), (e), \
+                        (c), (nc), \
+                        (f), (nf))
+
+static struct event_category cats[] = {
+       { .name = "cat0", },
+       { .name = "cat1", },
+};
+
+static void check_event_diff_cats(struct event_category *const *got,
+                                 unsigned int ngot, const char **exp,
+                                 unsigned int nexp)
+{
+       unsigned int i;
+
+       test_assert(ngot == nexp);
+
+       for (i = 0; i < nexp; i++)
+               test_assert(strcmp(got[i]->name, exp[i]) == 0);
+}
+
+static void check_event_diff_fields(const struct event_field *got, unsigned int ngot,
+                                   const struct event_field *exp, unsigned int nexp)
+{
+       unsigned int i;
+
+       test_assert(ngot == nexp);
+
+       for (i = 0; i < nexp; i++) {
+               if (got[i].value_type != exp[i].value_type) {
+                       test_assert(FALSE);
+                       continue;
+               }
+
+               switch (exp[i].value_type) {
+               case EVENT_FIELD_VALUE_TYPE_STR:
+                       test_assert(strcmp(exp[i].value.str,
+                                          got[i].value.str) == 0);
+                       break;
+               case EVENT_FIELD_VALUE_TYPE_INTMAX:
+                       test_assert(exp[i].value.intmax == got[i].value.intmax);
+                       break;
+               case EVENT_FIELD_VALUE_TYPE_TIMEVAL:
+                       test_assert(timeval_cmp(&exp[i].value.timeval,
+                                               &got[i].value.timeval) == 0);
+                       break;
+               }
+       }
+}
+
+static void check_event_diff(struct event *e, struct event *orig,
+                            const char **expected_cats,
+                            unsigned int num_expected_cats,
+                            const struct event_field *expected_fields,
+                            unsigned int num_expected_fields)
+{
+       struct event_category *const *cats;
+       const struct event_field *fields;
+       unsigned int num_cats;
+       unsigned int num_fields;
+
+       test_assert(e != orig);
+       test_assert(e->parent == NULL);
+
+       /* different pointers implies different ids */
+       test_assert(e->id != orig->id); /* TODO: does this make sense? */
+
+       test_assert(timeval_cmp(&e->tv_created_ioloop, &orig->tv_created_ioloop) == 0);
+       test_assert(timeval_cmp(&e->tv_created, &orig->tv_created) == 0);
+       test_assert(timeval_cmp(&e->tv_last_sent, &orig->tv_last_sent) == 0);
+
+       test_assert(strcmp(e->source_filename, orig->source_filename) == 0);
+       test_assert(e->source_linenum == orig->source_linenum);
+
+       /* FIXME: check sending name? */
+
+       cats = event_get_categories(e, &num_cats);
+       check_event_diff_cats(cats, num_cats,
+                             expected_cats, num_expected_cats);
+
+       fields = event_get_fields(e, &num_fields);
+       check_event_diff_fields(fields, num_fields,
+                               expected_fields, num_expected_fields);
+
+       event_unref(&e);
+}
+
+static void check_event_same(struct event *e, struct event *orig)
+{
+       test_assert(e == orig);
+
+       /* the pointers are the same; nothing can possibly differ */
+
+       event_unref(&e);
+}
+
+static void test_event_flatten_no_parent(void)
+{
+       struct event *e;
+
+       test_begin("event flatten: no parent");
+
+       e = event_create(NULL);
+
+       CHECK_FLATTEN_SAME(e);
+
+       event_add_int(e, "abc", 4);
+       CHECK_FLATTEN_SAME(e);
+
+       event_add_int(e, "def", 2);
+       CHECK_FLATTEN_SAME(e);
+
+       event_add_str(e, "abc", "foo");
+       CHECK_FLATTEN_SAME(e);
+
+       event_add_category(e, &cats[0]);
+       CHECK_FLATTEN_SAME(e);
+
+       event_unref(&e);
+
+       test_end();
+}
+
+static void test_event_flatten_one_parent(void)
+{
+       static const char *exp_1cat[] = {
+               "cat0",
+       };
+       static const char *exp_2cat[] = {
+               "cat1",
+               "cat0",
+       };
+       static struct event_field exp_int = {
+               .key = "abc",
+               .value_type = EVENT_FIELD_VALUE_TYPE_INTMAX,
+               .value.intmax = 42,
+       };
+       static struct event_field exp_2int[2] = {
+               {
+                       .key = "abc",
+                       .value_type = EVENT_FIELD_VALUE_TYPE_INTMAX,
+                       .value.intmax = 42,
+               },
+               {
+                       .key = "def",
+                       .value_type = EVENT_FIELD_VALUE_TYPE_INTMAX,
+                       .value.intmax = 49,
+               },
+       };
+       static struct event_field exp_1str1int[2] = {
+               {
+                       .key = "abc",
+                       .value_type = EVENT_FIELD_VALUE_TYPE_STR,
+                       .value.str = "foo",
+               },
+               {
+                       .key = "def",
+                       .value_type = EVENT_FIELD_VALUE_TYPE_INTMAX,
+                       .value.intmax = 49,
+               },
+       };
+       struct event *parent;
+       struct event *e;
+
+       test_begin("event flatten: one parent");
+
+       parent = event_create(NULL);
+
+       e = event_create(parent);
+
+       CHECK_FLATTEN_DIFF(e, NULL, 0, NULL, 0);
+
+       event_add_int(e, "abc", 42);
+       CHECK_FLATTEN_DIFF(e, NULL, 0, &exp_int, 1);
+
+       event_add_int(e, "def", 49);
+       CHECK_FLATTEN_DIFF(e, NULL, 0, exp_2int, 2);
+
+       event_add_str(e, "abc", "foo");
+       CHECK_FLATTEN_DIFF(e, NULL, 0, exp_1str1int, 2);
+
+       event_add_category(e, &cats[0]);
+       CHECK_FLATTEN_DIFF(e, exp_1cat, 1, exp_1str1int, 2);
+
+       event_add_category(e, &cats[1]);
+       CHECK_FLATTEN_DIFF(e, exp_2cat, 2, exp_1str1int, 2);
+
+       event_unref(&e);
+       event_unref(&parent);
+
+       test_end();
+}
+
+static void test_event_flatten_override_parent_field(void)
+{
+       static struct event_field exp_int = {
+               .key = "abc",
+               .value_type = EVENT_FIELD_VALUE_TYPE_INTMAX,
+               .value.intmax = 42,
+       };
+       static struct event_field exp_str = {
+               .key = "abc",
+               .value_type = EVENT_FIELD_VALUE_TYPE_STR,
+               .value.str = "def",
+       };
+       static struct event_field exp_2str[2] = {
+               {
+                       .key = "abc",
+                       .value_type = EVENT_FIELD_VALUE_TYPE_STR,
+                       .value.str = "def",
+               },
+               {
+                       .key = "foo",
+                       .value_type = EVENT_FIELD_VALUE_TYPE_STR,
+                       .value.str = "bar",
+               },
+       };
+       struct event *parent;
+       struct event *e;
+
+       test_begin("event flatten: override parent field");
+
+       parent = event_create(NULL);
+
+       event_add_int(parent, "abc", 5);
+
+       e = event_create(parent);
+
+       event_add_int(e, "abc", 42);
+
+       CHECK_FLATTEN_DIFF(e, NULL, 0, &exp_int, 1);
+
+       event_add_str(e, "abc", "def");
+       CHECK_FLATTEN_DIFF(e, NULL, 0, &exp_str, 1);
+
+       event_add_str(parent, "foo", "bar");
+       CHECK_FLATTEN_DIFF(e, NULL, 0, exp_2str, 2);
+
+       event_unref(&e);
+       event_unref(&parent);
+
+       test_end();
+}
+
+void test_event_flatten(void)
+{
+       test_event_flatten_no_parent();
+       test_event_flatten_one_parent();
+       test_event_flatten_override_parent_field();
+}
index e72357c7ab25725d830a477cd49349c3707e559f..12f9305bdb795a4a9aae1aaf509e1eaa86095162 100644 (file)
@@ -17,6 +17,7 @@ TEST(test_crc32)
 TEST(test_data_stack)
 FATAL(fatal_data_stack)
 TEST(test_event_filter)
+TEST(test_event_flatten)
 TEST(test_event_log)
 TEST(test_failures)
 TEST(test_file_create_locked)