Code

Merged branch 'master' of git://git.tokkee.org/sysdb.
authorSebastian Harl <sh@tokkee.org>
Tue, 10 Dec 2013 19:29:28 +0000 (20:29 +0100)
committerSebastian Harl <sh@tokkee.org>
Tue, 10 Dec 2013 19:29:28 +0000 (20:29 +0100)
src/core/store.c
t/Makefile.am
t/core/object_test.c
t/core/store_test.c [new file with mode: 0644]
t/libsysdb_net_test.c
t/libsysdb_test.c
t/libsysdb_test.h

index 8f5cae3..e9e0979 100644 (file)
@@ -418,11 +418,15 @@ sdb_store_attribute(const char *hostname, const char *key, const char *value,
                        /* stored object = */ SDB_ATTRIBUTE, key, last_update,
                        &updated_attr);
 
-       SDB_ATTR(updated_attr)->value = strdup(value);
-       if (! SDB_ATTR(updated_attr)->value) {
-               sdb_object_deref(SDB_OBJ(updated_attr));
-               status = -1;
+       if (status >= 0) {
+               assert(updated_attr);
+               SDB_ATTR(updated_attr)->value = strdup(value);
+               if (! SDB_ATTR(updated_attr)->value) {
+                       sdb_object_deref(SDB_OBJ(updated_attr));
+                       status = -1;
+               }
        }
+
        pthread_rwlock_unlock(&obj_lock);
        return status;
 } /* sdb_store_attribute */
@@ -533,7 +537,7 @@ sdb_store_tojson(sdb_strbuf_t *buf)
                }
 
                sdb_llist_iter_destroy(svc_iter);
-               sdb_strbuf_append(buf, "]}");
+               sdb_strbuf_append(buf, "]},");
        }
 
        sdb_strbuf_append(buf, "]}");
index c31ac50..87140cf 100644 (file)
@@ -7,6 +7,7 @@ check_PROGRAMS = libsysdb_test libsysdb_net_test
 libsysdb_test_SOURCES = \
                libsysdb_test.c libsysdb_test.h \
                core/object_test.c \
+               core/store_test.c \
                utils/channel_test.c \
                utils/dbi_test.c \
                utils/llist_test.c \
index f5b7bf6..e00d300 100644 (file)
@@ -52,30 +52,44 @@ obj_destroy_noop(sdb_object_t *obj)
        fail_unless(obj != NULL, "obj destroy function: received obj == NULL");
 } /* obj_destroy_noop */
 
-START_TEST(test_obj_create)
+struct noop {
+       sdb_object_t super;
+       int data;
+};
+static sdb_type_t noop_type = {
+       /* size = */ sizeof(struct noop),
+       /* init = */ obj_init_noop,
+       /* destroy = */ obj_destroy_noop,
+};
+
+static void *wrapped = (void *)0x42;
+
+static int destroy_wrapper_called = 0;
+static void
+wrapper_destroy(void *obj)
 {
-       struct noop {
-               sdb_object_t super;
-               int data;
-       };
-       sdb_type_t noop_type = {
-               /* size = */ sizeof(struct noop),
-               /* init = */ obj_init_noop,
-               /* destroy = */ obj_destroy_noop,
-       };
+       ++destroy_wrapper_called;
+       fail_unless(obj == wrapped,
+                       "wrapper_destroy received unexpected obj %p; expected: %p",
+                       obj, wrapped);
+} /* wrapper_destroy */
 
+START_TEST(test_obj_create)
+{
        sdb_object_t *obj;
+       sdb_type_t test_type = noop_type;
+
        const char *name = "test-object";
 
        init_noop_called = 0;
        init_noop_retval = 0;
        destroy_noop_called = 0;
-       obj = sdb_object_create(name, noop_type);
+       obj = sdb_object_create(name, test_type);
        fail_unless(obj != NULL,
                        "sdb_object_create() = NULL; expected: a new object");
-       fail_unless(obj->type.size == noop_type.size,
+       fail_unless(obj->type.size == test_type.size,
                        "after sdb_object_create(): type size mismatch; got: %zu; "
-                       "expected: %zu", obj->type.size, noop_type.size);
+                       "expected: %zu", obj->type.size, test_type.size);
        fail_unless(obj->ref_cnt == 1,
                        "after sdb_object_create(): obj->ref_cnt = %d; expected: 1",
                        obj->ref_cnt);
@@ -104,7 +118,7 @@ START_TEST(test_obj_create)
        init_noop_called = 0;
        init_noop_retval = -1;
        destroy_noop_called = 0;
-       obj = sdb_object_create(name, noop_type);
+       obj = sdb_object_create(name, test_type);
        fail_unless(obj == NULL,
                        "sdb_object_create() = %p; expected NULL (init returned -1)",
                        obj);
@@ -114,11 +128,11 @@ START_TEST(test_obj_create)
                        "sdb_object_create() did not call object's destroy function "
                        "after init failure");
 
-       noop_type.size = 1;
+       test_type.size = 1;
        init_noop_called = 0;
        init_noop_retval = 0;
        destroy_noop_called = 0;
-       obj = sdb_object_create(name, noop_type);
+       obj = sdb_object_create(name, test_type);
        fail_unless(obj == NULL,
                        "sdb_object_create() = %p; expected NULL (type's size too small)",
                        obj);
@@ -129,22 +143,157 @@ START_TEST(test_obj_create)
                        "sdb_object_create() called object's destroy function "
                        "when size was too small");
 
-       noop_type.size = sizeof(struct noop);
+       test_type.size = sizeof(struct noop);
        init_noop_retval = 0;
-       noop_type.init = NULL;
-       obj = sdb_object_create(name, noop_type);
+       test_type.init = NULL;
+       obj = sdb_object_create(name, test_type);
        fail_unless(obj != NULL,
                        "sdb_object_create() fails without init callback");
        sdb_object_deref(obj);
 
-       noop_type.destroy = NULL;
-       obj = sdb_object_create(name, noop_type);
+       test_type.destroy = NULL;
+       obj = sdb_object_create(name, test_type);
        fail_unless(obj != NULL,
                        "sdb_object_create() fails without destroy callback");
        sdb_object_deref(obj);
 }
 END_TEST
 
+START_TEST(test_obj_wrapper)
+{
+       sdb_object_t *obj;
+       const char *name = "wrapped-object";
+
+       destroy_wrapper_called = 0;
+       obj = sdb_object_create_wrapper(name, wrapped, wrapper_destroy);
+       fail_unless(obj != NULL,
+                       "sdb_object_create_wrapper() = NULL; expected: wrapper object");
+       fail_unless(obj->ref_cnt == 1,
+                       "after sdb_object_create_wrapper(); obj->ref_cnt = %d; "
+                       "expected: 1", obj->ref_cnt);
+       fail_unless(!strcmp(obj->name, name),
+                       "after sdb_object_create_wrapper(); obj->name = %s; expected: %s",
+                       obj->name, name);
+       fail_unless(obj->name != name,
+                       "sdb_object_create_wrapper() did not copy object name");
+       fail_unless(SDB_OBJ_WRAPPER(obj)->data == wrapped,
+                       "wrapped object wraps unexpected data %p; expected: %p",
+                       SDB_OBJ_WRAPPER(obj)->data, wrapped);
+
+       fail_unless(destroy_wrapper_called == 0,
+                       "sdb_object_create_wrapper() called object's destructor");
+
+       sdb_object_deref(obj);
+       fail_unless(destroy_wrapper_called == 1,
+                       "sdb_object_deref() did not call wrapped object's destructor");
+}
+END_TEST
+
+START_TEST(test_obj_ref)
+{
+       sdb_object_t *obj;
+       sdb_type_t test_type = noop_type;
+
+       init_noop_called = 0;
+       init_noop_retval = 0;
+       destroy_noop_called = 0;
+
+       obj = sdb_object_create("test-object", test_type);
+       fail_unless(obj != NULL,
+                       "sdb_object_create() = NULL; expected: valid object");
+
+       sdb_object_ref(obj);
+       fail_unless(obj->ref_cnt == 2,
+                       "after db_object_ref(): obj->ref_cnt = %d; expected: 2",
+                       obj->ref_cnt);
+
+       sdb_object_ref(obj);
+       fail_unless(obj->ref_cnt == 3,
+                       "after db_object_ref(): obj->ref_cnt = %d; expected: 3",
+                       obj->ref_cnt);
+
+       obj->ref_cnt = 42;
+       sdb_object_ref(obj);
+       fail_unless(obj->ref_cnt == 43,
+                       "after db_object_ref(): obj->ref_cnt = %d; expected: 43",
+                       obj->ref_cnt);
+
+       fail_unless(init_noop_called == 1,
+                       "after some sdb_object_ref(); object's init called %d times; "
+                       "expected: 1", init_noop_called);
+       fail_unless(destroy_noop_called == 0,
+                       "after some sdb_object_ref(); object's destroy called %d time%s; "
+                       "expected: 0", destroy_noop_called == 1 ? "" : "2",
+                       destroy_noop_called);
+
+       sdb_object_deref(obj);
+       fail_unless(obj->ref_cnt == 42,
+                       "after db_object_deref(): obj->ref_cnt = %d; expected: 42",
+                       obj->ref_cnt);
+
+       obj->ref_cnt = 23;
+       sdb_object_deref(obj);
+       fail_unless(obj->ref_cnt == 22,
+                       "after db_object_deref(): obj->ref_cnt = %d; expected: 22",
+                       obj->ref_cnt);
+
+       fail_unless(init_noop_called == 1,
+                       "after some sdb_object_{de,}ref(); object's init called %d times; "
+                       "expected: 1", init_noop_called);
+       fail_unless(destroy_noop_called == 0,
+                       "after some sdb_object_{de,}ref(); object's destroy called "
+                       "%d time%s; expected: 0", destroy_noop_called == 1 ? "" : "2",
+                       destroy_noop_called);
+
+       /* test_obj_create already checks the ref_cnt == 1 case */
+       obj->ref_cnt = 0;
+       sdb_object_deref(obj);
+       fail_unless(obj->ref_cnt <= 0,
+                       "after db_object_deref(): obj->ref_cnt = %d; expected: <= 0",
+                       obj->ref_cnt);
+
+       fail_unless(init_noop_called == 1,
+                       "after some sdb_object_{de,}ref(); object's init called %d times; "
+                       "expected: 1", init_noop_called);
+       fail_unless(destroy_noop_called == 1,
+                       "after some sdb_object_{de,}ref(); object's destroy called "
+                       "%d times; expected: 1", destroy_noop_called);
+
+       /* this should work */
+       sdb_object_deref(NULL);
+}
+END_TEST
+
+START_TEST(test_obj_cmp)
+{
+       sdb_object_t *obj1, *obj2, *obj3, *obj4;
+       int status;
+
+       obj1 = sdb_object_create("a", noop_type);
+       obj2 = sdb_object_create("b", noop_type);
+       obj3 = sdb_object_create("B", noop_type);
+       obj4 = sdb_object_create("c", noop_type);
+
+       status = sdb_object_cmp_by_name(obj1, obj2);
+       fail_unless(status == -1,
+                       "sdb_object_cmp_by_name('a', 'b') = %d; expected: -1", status);
+       status = sdb_object_cmp_by_name(obj2, obj3);
+       fail_unless(status == 0,
+                       "sdb_object_cmp_by_name('b', 'B') = %d; expected: 0", status);
+       status = sdb_object_cmp_by_name(obj4, obj3);
+       fail_unless(status == 1,
+                       "sdb_object_cmp_by_name('c', 'B') = %d; expected: 1", status);
+       status = sdb_object_cmp_by_name(obj1, obj1);
+       fail_unless(status == 0,
+                       "sdb_object_cmp_by_name('a', 'a') = %d; expected: 0", status);
+
+       sdb_object_deref(obj1);
+       sdb_object_deref(obj2);
+       sdb_object_deref(obj3);
+       sdb_object_deref(obj4);
+}
+END_TEST
+
 Suite *
 core_object_suite(void)
 {
@@ -153,6 +302,9 @@ core_object_suite(void)
 
        tc = tcase_create("core");
        tcase_add_test(tc, test_obj_create);
+       tcase_add_test(tc, test_obj_wrapper);
+       tcase_add_test(tc, test_obj_ref);
+       tcase_add_test(tc, test_obj_cmp);
        suite_add_tcase(s, tc);
 
        return s;
diff --git a/t/core/store_test.c b/t/core/store_test.c
new file mode 100644 (file)
index 0000000..afc5323
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * SysDB - t/core/store_test.c
+ * Copyright (C) 2013 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "core/store.h"
+#include "libsysdb_test.h"
+
+#include <check.h>
+#include <string.h>
+
+START_TEST(test_store_host)
+{
+       struct {
+               const char *name;
+               sdb_time_t  last_update;
+               int         expected;
+       } golden_data[] = {
+               { "a", 2, 0 },
+               { "a", 3, 0 },
+               { "a", 1, 1 },
+               { "b", 2, 0 },
+               { "b", 1, 1 },
+               { "A", 1, 1 }, /* case-insensitive */
+               { "A", 4, 0 },
+       };
+
+       struct {
+               const char *name;
+               _Bool       has;
+       } golden_hosts[] = {
+               { "a", 1 == 1 },
+               { "b", 1 == 1 },
+               { "c", 0 == 1 },
+               { "A", 1 == 1 },
+       };
+
+       size_t i;
+
+       for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+               int status;
+
+               status = sdb_store_host(golden_data[i].name,
+                               golden_data[i].last_update);
+               fail_unless(status == golden_data[i].expected,
+                               "sdb_store_host(%s, %d) = %d; expected: %d",
+                               golden_data[i].name, (int)golden_data[i].last_update,
+                               status, golden_data[i].expected);
+       }
+
+       for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_hosts); ++i) {
+               _Bool has;
+
+               has = sdb_store_has_host(golden_hosts[i].name);
+               fail_unless(has == golden_hosts[i].has,
+                               "sdb_store_has_host(%s) = %d; expected: %d",
+                               golden_hosts[i].name, has, golden_hosts[i].has);
+       }
+}
+END_TEST
+
+START_TEST(test_store_attr)
+{
+       struct {
+               const char *host;
+               const char *key;
+               const char *value;
+               sdb_time_t  last_update;
+               int         expected;
+       } golden_data[] = {
+               { "k", "k", "v", 1, -1 },
+               { "k", "k", "v", 1, -1 }, /* retry to ensure the host is not created */
+               { "l", "k1", "v1", 1, 0 },
+               { "l", "k1", "v2", 2, 0 },
+               { "l", "k1", "v3", 1, 1 },
+               { "l", "k2", "v1", 1, 0 },
+               { "m", "k", "v1", 2, 0 },
+               { "m", "k", "v2", 1, 1 },
+       };
+
+       size_t i;
+
+       sdb_store_host("l", 1);
+       sdb_store_host("m", 1);
+       for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+               int status;
+
+               status = sdb_store_attribute(golden_data[i].host,
+                               golden_data[i].key, golden_data[i].value,
+                               golden_data[i].last_update);
+               fail_unless(status == golden_data[i].expected,
+                               "sdb_store_attribute(%s, %s, %s, %d) = %d; expected: %d",
+                               golden_data[i].host, golden_data[i].key, golden_data[i].value,
+                               golden_data[i].last_update, status, golden_data[i].expected);
+       }
+}
+END_TEST
+
+START_TEST(test_store_service)
+{
+       struct {
+               const char *host;
+               const char *svc;
+               sdb_time_t  last_update;
+               int         expected;
+       } golden_data[] = {
+               { "k", "s", 1, -1 },
+               { "k", "s", 1, -1 }, /* retry to ensure the host is not created */
+               { "l", "s1", 1, 0 },
+               { "l", "s1", 2, 0 },
+               { "l", "s1", 1, 1 },
+               { "l", "s2", 1, 0 },
+               { "m", "s", 2, 0 },
+               { "m", "s", 1, 1 },
+       };
+
+       size_t i;
+
+       sdb_store_host("m", 1);
+       sdb_store_host("l", 1);
+       for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+               int status;
+
+               status = sdb_store_service(golden_data[i].host,
+                               golden_data[i].svc, golden_data[i].last_update);
+               fail_unless(status == golden_data[i].expected,
+                               "sdb_store_attribute(%s, %s, %d) = %d; expected: %d",
+                               golden_data[i].host, golden_data[i].svc,
+                               golden_data[i].last_update, status, golden_data[i].expected);
+       }
+}
+END_TEST
+
+START_TEST(test_store_tojson)
+{
+       sdb_strbuf_t *buf;
+
+       int status, pos;
+       size_t len1, len2;
+       size_t i;
+
+       const char *expected = "{\"hosts\":["
+               "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
+                       "\"attributes\": ["
+                               "{\"name\": \"k1\", \"value\": \"v1\", \"last_update\": \"1970-01-01 00:00:00 +0000\"},"
+                               "{\"name\": \"k2\", \"value\": \"v2\", \"last_update\": \"1970-01-01 00:00:00 +0000\"},"
+                               "{\"name\": \"k3\", \"value\": \"v3\", \"last_update\": \"1970-01-01 00:00:00 +0000\"},"
+                       "], "
+                       "\"services\": []},"
+               "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
+                       "\"attributes\": [], "
+                       "\"services\": ["
+                               "{\"name\": \"s1\", \"last_update\": \"1970-01-01 00:00:00 +0000\"},"
+                               "{\"name\": \"s2\", \"last_update\": \"1970-01-01 00:00:00 +0000\"},"
+                       "]},"
+       "]}";
+
+       sdb_store_host("h1", 1);
+       sdb_store_host("h2", 1);
+
+       sdb_store_attribute("h1", "k1", "v1", 1);
+       sdb_store_attribute("h1", "k2", "v2", 1);
+       sdb_store_attribute("h1", "k3", "v3", 1);
+
+       sdb_store_service("h2", "s1", 1);
+       sdb_store_service("h2", "s2", 1);
+
+       buf = sdb_strbuf_create(0);
+       status = sdb_store_tojson(buf);
+       fail_unless(status == 0,
+                       "sdb_store_tojson() = %d; expected: 0", status);
+
+       len1 = strlen(sdb_strbuf_string(buf));
+       len2 = strlen(expected);
+
+       pos = -1;
+       if (len1 != len2)
+               pos = (int)(len1 <= len2 ? len1 : len2);
+
+       for (i = 0; i < (len1 <= len2 ? len1 : len2); ++i) {
+               if (sdb_strbuf_string(buf)[i] != expected[i]) {
+                       pos = (int)i;
+                       break;
+               }
+       }
+
+       fail_unless(pos == -1,
+                       "sdb_store_tojson() returned unexpected result\n"
+                       "         got: %s\n              %*s\n    expected: %s",
+                       sdb_strbuf_string(buf), pos + 1, "^", expected);
+}
+END_TEST
+
+Suite *
+core_store_suite(void)
+{
+       Suite *s = suite_create("core::store");
+       TCase *tc;
+
+       tc = tcase_create("core");
+       /* test this first to ensure the store is empty
+        * even when using CK_NOFORK */
+       tcase_add_test(tc, test_store_tojson);
+       tcase_add_test(tc, test_store_host);
+       tcase_add_test(tc, test_store_attr);
+       tcase_add_test(tc, test_store_service);
+       suite_add_tcase(s, tc);
+
+       return s;
+} /* core_store_suite */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
index aede0d6..320cfac 100644 (file)
@@ -33,6 +33,7 @@
 
 #include <check.h>
 #include <stdio.h>
+#include <stdlib.h>
 
 int
 main(void)
@@ -48,6 +49,8 @@ main(void)
 #endif /* HAVE_FOPENCOOKIE */
        };
 
+       putenv("TZ=UTC");
+
        for (i = 0; i < SDB_STATIC_ARRAY_LEN(creators); ++i) {
                SRunner *sr;
                Suite *s;
index 91909b8..ca2a22d 100644 (file)
@@ -29,6 +29,7 @@
 
 #include <check.h>
 #include <stdio.h>
+#include <stdlib.h>
 
 int
 main(void)
@@ -38,12 +39,15 @@ main(void)
 
        suite_creator_t creators[] = {
                { core_object_suite, NULL },
+               { core_store_suite, NULL },
                { util_channel_suite, NULL },
                { util_dbi_suite, NULL },
                { util_llist_suite, NULL },
                { util_strbuf_suite, NULL },
        };
 
+       putenv("TZ=UTC");
+
        for (i = 0; i < SDB_STATIC_ARRAY_LEN(creators); ++i) {
                SRunner *sr;
                Suite *s;
index eadeca5..f9fddeb 100644 (file)
@@ -63,6 +63,10 @@ typedef struct {
 Suite *
 core_object_suite(void);
 
+/* t/core/store_test */
+Suite *
+core_store_suite(void);
+
 /* t/utils/channel_test */
 Suite *
 util_channel_suite(void);