summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 3ba8122)
raw | patch | inline | side by side (parent: 3ba8122)
author | Sebastian Harl <sh@tokkee.org> | |
Sat, 26 Apr 2014 20:42:47 +0000 (22:42 +0200) | ||
committer | Sebastian Harl <sh@tokkee.org> | |
Sat, 26 Apr 2014 20:42:47 +0000 (22:42 +0200) |
38 files changed:
diff --git a/.gitignore b/.gitignore
index 9f1fd8ff4e3ca14646e8733b29d48b78738fa7d2..0657cd0d112e8ac2404ed3a098785c4c5c9d1085 100644 (file)
--- a/.gitignore
+++ b/.gitignore
sysdb.h
# unit testing output
-t/libsysdb_test
-t/libsysdb_net_test
-t/*.log
-t/*.trs
+t/unit/libsysdb_test
+t/unit/libsysdb_net_test
+t/unit/*.log
+t/unit/*.trs
test-driver
diff --git a/t/Makefile.am b/t/Makefile.am
index 551d8639025caa351361d98a5d2c0408dc953f0f..4764a6ed276d5fdd9a71469975816f75388dd5e3 100644 (file)
--- a/t/Makefile.am
+++ b/t/Makefile.am
AM_LDFLAGS = @COVERAGE_LDFLAGS@
AM_CPPFLAGS = -I$(top_srcdir)/src/include
-TESTS = libsysdb_test libsysdb_net_test
-check_PROGRAMS = libsysdb_test libsysdb_net_test
+TESTS = unit/libsysdb_test unit/libsysdb_net_test
+check_PROGRAMS = unit/libsysdb_test unit/libsysdb_net_test
-libsysdb_test_SOURCES = \
- libsysdb_test.c libsysdb_test.h \
- libsysdb_testutils.c libsysdb_testutils.h \
- core/data_test.c \
- core/object_test.c \
- core/store_test.c \
- core/store_lookup_test.c \
- core/time_test.c \
- frontend/connection_test.c \
- frontend/parser_test.c \
- frontend/sock_test.c \
- utils/channel_test.c \
- utils/dbi_test.c \
- utils/llist_test.c \
- utils/strbuf_test.c
-libsysdb_test_CFLAGS = $(AM_CFLAGS) @CHECK_CFLAGS@
-libsysdb_test_LDADD = $(top_builddir)/src/libsysdb.la @CHECK_LIBS@
+unit_libsysdb_test_SOURCES = \
+ unit/libsysdb_test.c unit/libsysdb_test.h \
+ unit/libsysdb_testutils.c unit/libsysdb_testutils.h \
+ unit/core/data_test.c \
+ unit/core/object_test.c \
+ unit/core/store_test.c \
+ unit/core/store_lookup_test.c \
+ unit/core/time_test.c \
+ unit/frontend/connection_test.c \
+ unit/frontend/parser_test.c \
+ unit/frontend/sock_test.c \
+ unit/utils/channel_test.c \
+ unit/utils/dbi_test.c \
+ unit/utils/llist_test.c \
+ unit/utils/strbuf_test.c
+unit_libsysdb_test_CFLAGS = $(AM_CFLAGS) @CHECK_CFLAGS@ -I$(top_srcdir)/t/unit
+unit_libsysdb_test_LDADD = $(top_builddir)/src/libsysdb.la @CHECK_LIBS@
-libsysdb_net_test_SOURCES = \
- libsysdb_net_test.c libsysdb_test.h \
- libsysdb_testutils.c libsysdb_testutils.h
+unit_libsysdb_net_test_SOURCES = \
+ unit/libsysdb_net_test.c unit/libsysdb_test.h \
+ unit/libsysdb_testutils.c unit/libsysdb_testutils.h
if BUILD_WITH_FOPENCOOKIE
-libsysdb_net_test_SOURCES += utils/unixsock_test.c
+unit_libsysdb_net_test_SOURCES += unit/utils/unixsock_test.c
endif
-libsysdb_net_test_CFLAGS = $(AM_CFLAGS) @CHECK_CFLAGS@
-libsysdb_net_test_LDADD = $(top_builddir)/src/libsysdb.la @CHECK_LIBS@
+unit_libsysdb_net_test_CFLAGS = $(AM_CFLAGS) @CHECK_CFLAGS@ -I$(top_srcdir)/t/unit
+unit_libsysdb_net_test_LDADD = $(top_builddir)/src/libsysdb.la @CHECK_LIBS@
test: check
diff --git a/t/core/data_test.c b/t/core/data_test.c
--- a/t/core/data_test.c
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * SysDB - t/core/data_test.c
- * Copyright (C) 2014 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/data.h"
-#include "libsysdb_test.h"
-
-#include <check.h>
-
-START_TEST(test_data)
-{
- sdb_data_t d1, d2;
- int check;
-
- d2.type = SDB_TYPE_INTEGER;
- d2.data.integer = 4711;
- check = sdb_data_copy(&d1, &d2);
- fail_unless(!check, "sdb_data_copy() = %i; expected: 0", check);
- fail_unless(d1.type == d2.type,
- "sdb_data_copy() didn't copy type; got: %i; expected: %i",
- d1.type, d2.type);
- fail_unless(d1.data.integer == d2.data.integer,
- "sdb_data_copy() didn't copy integer data: got: %d; expected: %d",
- d1.data.integer, d2.data.integer);
-
- d2.type = SDB_TYPE_DECIMAL;
- d2.data.decimal = 47.11;
- check = sdb_data_copy(&d1, &d2);
- fail_unless(!check, "sdb_data_copy() = %i; expected: 0", check);
- fail_unless(d1.type == d2.type,
- "sdb_data_copy() didn't copy type; got: %i; expected: %i",
- d1.type, d2.type);
- fail_unless(d1.data.decimal == d2.data.decimal,
- "sdb_data_copy() didn't copy decimal data: got: %f; expected: %f",
- d1.data.decimal, d2.data.decimal);
-
- d2.type = SDB_TYPE_STRING;
- d2.data.string = "some string";
- check = sdb_data_copy(&d1, &d2);
- fail_unless(!check, "sdb_data_copy() = %i; expected: 0", check);
- fail_unless(d1.type == d2.type,
- "sdb_data_copy() didn't copy type; got: %i; expected: %i",
- d1.type, d2.type);
- fail_unless(!strcmp(d1.data.string, d2.data.string),
- "sdb_data_copy() didn't copy string data: got: %s; expected: %s",
- d1.data.string, d2.data.string);
-
- sdb_data_free_datum(&d1);
- fail_unless(d1.data.string == NULL,
- "sdb_data_free_datum() didn't free string data");
-
- d2.type = SDB_TYPE_DATETIME;
- d2.data.datetime = 4711;
- check = sdb_data_copy(&d1, &d2);
- fail_unless(!check, "sdb_data_copy() = %i; expected: 0", check);
- fail_unless(d1.type == d2.type,
- "sdb_data_copy() didn't copy type; got: %i; expected: %i",
- d1.type, d2.type);
- fail_unless(d1.data.datetime == d2.data.datetime,
- "sdb_data_copy() didn't copy datetime data: got: %d; expected: %d",
- d1.data.datetime, d2.data.datetime);
-
- d2.type = SDB_TYPE_BINARY;
- d2.data.binary.datum = (unsigned char *)"some string";
- d2.data.binary.length = strlen((const char *)d2.data.binary.datum);
- check = sdb_data_copy(&d1, &d2);
- fail_unless(!check, "sdb_data_copy() = %i; expected: 0", check);
- fail_unless(d1.type == d2.type,
- "sdb_data_copy() didn't copy type; got: %i; expected: %i",
- d1.type, d2.type);
- fail_unless(d1.data.binary.length == d2.data.binary.length,
- "sdb_data_copy() didn't copy length; got: %d; expected: 5d",
- d1.data.binary.length, d2.data.binary.length);
- fail_unless(!memcmp(d1.data.binary.datum, d2.data.binary.datum,
- d2.data.binary.length),
- "sdb_data_copy() didn't copy binary data: got: %s; expected: %s",
- d1.data.string, d2.data.string);
-
- sdb_data_free_datum(&d1);
- fail_unless(d1.data.binary.length == 0,
- "sdb_data_free_datum() didn't reset binary datum length");
- fail_unless(d1.data.binary.datum == NULL,
- "sdb_data_free_datum() didn't free binary datum");
-}
-END_TEST
-
-START_TEST(test_format)
-{
- struct {
- sdb_data_t datum;
- const char *expected;
- } golden_data[] = {
- {
- { SDB_TYPE_INTEGER, { .integer = 4711 } },
- "4711",
- },
- {
- { SDB_TYPE_DECIMAL, { .decimal = 65536.0 } },
- "0x1p+16",
- },
- {
- { SDB_TYPE_STRING, { .string = NULL } },
- "\"NULL\"",
- },
- {
- { SDB_TYPE_STRING, { .string = "this is a test" } },
- "\"this is a test\"",
- },
- {
- { SDB_TYPE_STRING, { .string = "special \\ \" characters" } },
- "\"special \\\\ \\\" characters\"",
- },
- {
- { SDB_TYPE_DATETIME, { .datetime= 471147114711471100 } },
- "\"1984-12-06 02:11:54 +0000\"",
- },
- {
- { SDB_TYPE_BINARY, { .binary = { 0, NULL } } },
- "\"\"",
- },
- {
- {
- SDB_TYPE_BINARY,
- { .binary = { 12, (unsigned char *)"binary\0crap\x42" } },
- },
- "\"\\x62\\x69\\x6e\\x61\\x72\\x79\\x0\\x63\\x72\\x61\\x70\\x42\"",
- },
- };
-
- size_t i;
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- sdb_data_t *datum = &golden_data[i].datum;
- char buf[sdb_data_strlen(datum) + 2];
- int check;
-
- memset(buf, (int)'A', sizeof(buf));
-
- check = sdb_data_format(datum, buf, sizeof(buf) - 1,
- SDB_DOUBLE_QUOTED);
- fail_unless(check > 0,
- "sdb_data_format(type=%s) = %d; expected: >0",
- SDB_TYPE_TO_STRING(datum->type), check);
- fail_unless(! strcmp(buf, golden_data[i].expected),
- "sdb_data_format(type=%s) used wrong format: %s; expected: %s",
- SDB_TYPE_TO_STRING(datum->type), buf, golden_data[i].expected);
-
- fail_unless((size_t)check <= sizeof(buf) - 2,
- "sdb_data_format(type=%s) wrote %d bytes; "
- "expected <= %zu based on sdb_data_strlen()",
- SDB_TYPE_TO_STRING(datum->type), check, sizeof(buf) - 2);
-
- fail_unless(buf[sizeof(buf) - 2] == '\0',
- "sdb_data_format(type=%s) did not nul-terminate the buffer",
- SDB_TYPE_TO_STRING(datum->type));
- fail_unless(buf[sizeof(buf) - 1] == 'A',
- "sdb_data_format(type=%s) wrote past the end of the buffer",
- SDB_TYPE_TO_STRING(datum->type));
- }
-}
-END_TEST
-
-Suite *
-core_data_suite(void)
-{
- Suite *s = suite_create("core::data");
- TCase *tc;
-
- tc = tcase_create("core");
- tcase_add_test(tc, test_data);
- tcase_add_test(tc, test_format);
- suite_add_tcase(s, tc);
-
- return s;
-} /* core_data_suite */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-
diff --git a/t/core/object_test.c b/t/core/object_test.c
--- a/t/core/object_test.c
+++ /dev/null
@@ -1,340 +0,0 @@
-/*
- * SysDB - t/core/object_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/object.h"
-#include "libsysdb_test.h"
-
-#include <check.h>
-
-/*
- * private data types
- */
-
-static int init_noop_called = 0;
-static int init_noop_retval = 0;
-static int
-obj_init_noop(sdb_object_t *obj, va_list __attribute__((unused)) ap)
-{
- ++init_noop_called;
- fail_unless(obj != NULL, "obj init function: received obj == NULL");
- return init_noop_retval;
-} /* obj_init_noop */
-
-static int destroy_noop_called = 0;
-static void
-obj_destroy_noop(sdb_object_t *obj)
-{
- ++destroy_noop_called;
- fail_unless(obj != NULL, "obj destroy function: received obj == NULL");
-} /* obj_destroy_noop */
-
-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)
-{
- ++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, test_type);
- fail_unless(obj != NULL,
- "sdb_object_create() = NULL; expected: a new object");
- fail_unless(obj->type.size == test_type.size,
- "after sdb_object_create(): type size mismatch; got: %zu; "
- "expected: %zu", obj->type.size, test_type.size);
- fail_unless(obj->type.init == obj_init_noop,
- "after sdb_object_create(): type init = %p; exptected: %p",
- obj->type.init, obj_init_noop);
- fail_unless(obj->type.destroy == obj_destroy_noop,
- "after sdb_object_create(): type destroy = %p; exptected: %p",
- obj->type.destroy, obj_destroy_noop);
- fail_unless(obj->ref_cnt == 1,
- "after sdb_object_create(): obj->ref_cnt = %d; expected: 1",
- obj->ref_cnt);
- fail_unless(!strcmp(obj->name, "test-object"),
- "after sdb_object_create(): obj->name = '%s'; expected: '%s'",
- obj->name, name);
- fail_unless(obj->name != name,
- "after sdb_object_create(): obj->name was not strdup()'ed");
-
- fail_unless(init_noop_called == 1,
- "sdb_object_create() did not call object's init function");
- fail_unless(destroy_noop_called == 0,
- "sdb_object_create() called object's destroy function");
- fail_unless(((struct noop *)obj)->data == 0,
- "sdb_object_create() did not initialize data to zero");
-
- sdb_object_deref(obj);
- fail_unless(destroy_noop_called == 1,
- "sdb_object_deref() did not call object's destroy function");
-
- init_noop_called = 0;
- init_noop_retval = -1;
- destroy_noop_called = 0;
- obj = sdb_object_create(name, test_type);
- fail_unless(obj == NULL,
- "sdb_object_create() = %p; expected NULL (init returned -1)",
- obj);
- fail_unless(init_noop_called == 1,
- "sdb_object_create() did not call object's init function");
- fail_unless(destroy_noop_called == 1,
- "sdb_object_create() did not call object's destroy function "
- "after init failure");
-
- test_type.size = 1;
- init_noop_called = 0;
- init_noop_retval = 0;
- destroy_noop_called = 0;
- obj = sdb_object_create(name, test_type);
- fail_unless(obj == NULL,
- "sdb_object_create() = %p; expected NULL (type's size too small)",
- obj);
- fail_unless(init_noop_called == 0,
- "sdb_object_create() called object's init function "
- "when size was too small");
- fail_unless(destroy_noop_called == 0,
- "sdb_object_create() called object's destroy function "
- "when size was too small");
-
- test_type.size = sizeof(struct noop);
- init_noop_retval = 0;
- 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);
-
- 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);
-
- init_noop_called = 0;
- obj = sdb_object_create_simple(name, sizeof(struct noop), NULL);
- fail_unless(obj != NULL,
- "sdb_object_create_simple() = NULL; expected: <obj>");
- fail_unless(obj->type.size == sizeof(struct noop),
- "sdb_object_create_simple() created object of size %zu; "
- "expected: %zu", obj->type.size, sizeof(struct noop));
- fail_unless(obj->type.init == NULL,
- "sdb_object_create_simple() did not set init=NULL");
- fail_unless(obj->type.destroy == NULL,
- "sdb_object_create_simple() did not set destroy=NULL");
- fail_unless(init_noop_called == 0,
- "sdb_object_create_simple() unexpectedly called noop's init");
- sdb_object_deref(obj);
-
- obj = sdb_object_create_T(NULL, struct noop);
- fail_unless(obj != NULL,
- "sdb_object_create_simple() = NULL; expected: <obj>");
- fail_unless(obj->type.size == sizeof(struct noop),
- "sdb_object_create_simple() created object of size %zu; "
- "expected: %zu", obj->type.size, sizeof(struct noop));
- fail_unless(obj->type.init == NULL,
- "sdb_object_create_simple() did not set init=NULL");
- fail_unless(obj->type.destroy == NULL,
- "sdb_object_create_simple() did not set destroy=NULL");
- fail_unless(init_noop_called == 0,
- "sdb_object_create_simple() unexpectedly called noop's init");
- 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(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)
-{
- Suite *s = suite_create("core::object");
- TCase *tc;
-
- 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;
-} /* core_object_suite */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-
diff --git a/t/core/store_lookup_test.c b/t/core/store_lookup_test.c
+++ /dev/null
@@ -1,535 +0,0 @@
-/*
- * SysDB - t/core/store_lookup_test.c
- * Copyright (C) 2014 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 "core/store-private.h"
-#include "frontend/parser.h"
-#include "libsysdb_test.h"
-
-#include <check.h>
-#include <string.h>
-
-static void
-populate(void)
-{
- const char *hosts[] = { "a", "b", "c" };
-
- struct {
- const char *host;
- const char *service;
- } services[] = {
- { "a", "s1" },
- { "a", "s2" },
- { "b", "s1" },
- { "b", "s3" },
- };
-
- struct {
- const char *host;
- const char *name;
- sdb_data_t value;
- } attrs[] = {
- { "a", "k1", { SDB_TYPE_STRING, { .string = "v1" } } },
- };
-
- size_t i;
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(hosts); ++i) {
- int status = sdb_store_host(hosts[i], 1);
- fail_unless(status == 0,
- "sdb_store_host(%s, 1) = %d; expected: 0",
- hosts[i], status);
- }
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(services); ++i) {
- int status = sdb_store_service(services[i].host,
- services[i].service, 1);
- fail_unless(status == 0,
- "sdb_store_service(%s, %s, 1) = %d; expected: 0",
- services[i].host, services[i].service, status);
- }
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(attrs); ++i) {
- int status = sdb_store_attribute(attrs[i].host,
- attrs[i].name, &attrs[i].value, 1);
- fail_unless(status == 0,
- "sdb_store_attribute(%s, %s, <val>, 1) = %d; expected: 0",
- attrs[i].host, attrs[i].name, status);
- }
-} /* populate */
-
-START_TEST(test_store_match)
-{
- sdb_store_base_t *obj;
-
- struct {
- const char *hostname;
- const char *hostname_re;
-
- const char *service_name;
- const char *service_name_re;
-
- const char *attr_name;
- const char *attr_name_re;
- const char *attr_value;
- const char *attr_value_re;
-
- int expected;
- } golden_data[] = {
- {
- /* host */ NULL, NULL,
- /* svc */ NULL, NULL,
- /* attr */ NULL, NULL, NULL, NULL, 1
- },
- {
- /* host */ "a", NULL,
- /* svc */ NULL, NULL,
- /* attr */ NULL, NULL, NULL, NULL, 1
- },
- {
- /* host */ "b", NULL,
- /* svc */ NULL, NULL,
- /* attr */ NULL, NULL, NULL, NULL, 0
- },
- {
- /* host */ NULL, "^a$",
- /* svc */ NULL, NULL,
- /* attr */ NULL, NULL, NULL, NULL, 1
- },
- {
- /* host */ NULL, "^b$",
- /* svc */ NULL, NULL,
- /* attr */ NULL, NULL, NULL, NULL, 0
- },
- {
- /* host */ "a", "^a$",
- /* svc */ NULL, NULL,
- /* attr */ NULL, NULL, NULL, NULL, 1
- },
- {
- /* host */ "a", "^b$",
- /* svc */ NULL, NULL,
- /* attr */ NULL, NULL, NULL, NULL, 0
- },
- {
- /* host */ "b", "^a$",
- /* svc */ NULL, NULL,
- /* attr */ NULL, NULL, NULL, NULL, 0
- },
- {
- /* host */ "a", "^a$",
- /* svc */ "s1", NULL,
- /* attr */ NULL, NULL, NULL, NULL, 1
- },
- {
- /* host */ "a", "^a$",
- /* svc */ NULL, "^s1$",
- /* attr */ NULL, NULL, NULL, NULL, 1
- },
- {
- /* host */ "a", "^a$",
- /* svc */ "s1", "^s1$",
- /* attr */ NULL, NULL, NULL, NULL, 1
- },
- {
- /* host */ "a", "^a$",
- /* svc */ "x1", NULL,
- /* attr */ NULL, NULL, NULL, NULL, 0
- },
- {
- /* host */ "a", "^a$",
- /* svc */ NULL, "x",
- /* attr */ NULL, NULL, NULL, NULL, 0
- },
- {
- /* host */ "a", "^a$",
- /* svc */ "x1", "x",
- /* attr */ NULL, NULL, NULL, NULL, 0
- },
- {
- /* host */ "a", "^a$",
- /* svc */ "s1", "x",
- /* attr */ NULL, NULL, NULL, NULL, 0
- },
- {
- /* host */ "a", "^a$",
- /* svc */ "x1", "s",
- /* attr */ NULL, NULL, NULL, NULL, 0
- },
- {
- /* host */ "a", "^a$",
- /* svc */ "s1", "^s1$",
- /* attr */ "k1", NULL, NULL, NULL, 1
- },
- {
- /* host */ "a", "^a$",
- /* svc */ "s1", "^s1$",
- /* attr */ NULL, "^k", NULL, NULL, 1
- },
- {
- /* host */ "a", "^a$",
- /* svc */ "s1", "^s1$",
- /* attr */ NULL, NULL, "v1", NULL, 1
- },
- {
- /* host */ "a", "^a$",
- /* svc */ "s1", "^s1$",
- /* attr */ NULL, NULL, NULL, "^v1$", 1
- },
- {
- /* host */ "a", "^a$",
- /* svc */ "s1", "^s1$",
- /* attr */ "k1", "1", "v1", "1", 1
- },
- };
-
- size_t i;
-
- obj = sdb_store_get_host("a");
- fail_unless(obj != NULL,
- "sdb_store_get_host(a) = NULL; expected: <host>");
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- sdb_store_matcher_t *h, *s, *a, *n;
- int status;
-
- s = sdb_store_service_matcher(golden_data[i].service_name,
- golden_data[i].service_name_re, NULL);
- fail_unless(s != NULL,
- "sdb_store_service_matcher() = NULL; expected: <matcher>");
-
- a = sdb_store_attr_matcher(golden_data[i].attr_name,
- golden_data[i].attr_name_re, golden_data[i].attr_value,
- golden_data[i].attr_value_re);
- fail_unless(a != NULL,
- "sdb_store_attr_matcher() = NULL; expected: <matcher>");
-
- h = sdb_store_host_matcher(golden_data[i].hostname,
- golden_data[i].hostname_re, s, a);
- fail_unless(h != NULL,
- "sdb_store_host_matcher() = NULL: expected: <matcher>");
- /* pass ownership to the host matcher */
- sdb_object_deref(SDB_OBJ(s));
- sdb_object_deref(SDB_OBJ(a));
-
- status = sdb_store_matcher_matches(h, obj);
- fail_unless(status == golden_data[i].expected,
- "sdb_store_matcher_matches({{%s, %s},{%s, %s},"
- "{%s, %s, %s, %s}}, <host a>) = %d; expected: %d",
- golden_data[i].hostname, golden_data[i].hostname_re,
- golden_data[i].service_name, golden_data[i].service_name_re,
- golden_data[i].attr_name, golden_data[i].attr_name_re,
- golden_data[i].attr_value, golden_data[i].attr_value_re,
- status, golden_data[i].expected);
-
- n = sdb_store_inv_matcher(h);
- fail_unless(n != NULL,
- "sdb_store_inv_matcher() = NULL; expected: <matcher>");
- sdb_object_deref(SDB_OBJ(h));
-
- /* now match the inverted set of objects */
- status = sdb_store_matcher_matches(n, obj);
- fail_unless(status == !golden_data[i].expected,
- "sdb_store_matcher_matches(NOT{{%s, %s},{%s, %s},"
- "{%s, %s, %s, %s}}, <host a>) = %d; expected: %d",
- golden_data[i].hostname, golden_data[i].hostname_re,
- golden_data[i].service_name, golden_data[i].service_name_re,
- golden_data[i].attr_name, golden_data[i].attr_name_re,
- golden_data[i].attr_value, golden_data[i].attr_value_re,
- status, !golden_data[i].expected);
-
- sdb_object_deref(SDB_OBJ(n));
- }
-
- sdb_object_deref(SDB_OBJ(obj));
-}
-END_TEST
-
-START_TEST(test_store_match_op)
-{
- sdb_store_base_t *obj;
-
- sdb_store_matcher_t *always = sdb_store_host_matcher(NULL, NULL, NULL, NULL);
- sdb_store_matcher_t *never = sdb_store_host_matcher("a", "b", NULL, NULL);
-
- struct {
- const char *op;
- sdb_store_matcher_t *left;
- sdb_store_matcher_t *right;
- int expected;
- } golden_data[] = {
- { "OR", always, always, 1 },
- { "OR", always, never, 1 },
- { "OR", never, always, 1 },
- { "OR", never, never, 0 },
- { "AND", always, always, 1 },
- { "AND", always, never, 0 },
- { "AND", never, always, 0 },
- { "AND", never, never, 0 },
- };
-
- int status;
- size_t i;
-
- obj = sdb_store_get_host("a");
-
- status = sdb_store_matcher_matches(always, obj);
- fail_unless(status == 1,
- "INTERNAL ERROR: 'always' did not match host");
- status = sdb_store_matcher_matches(never, obj);
- fail_unless(status == 0,
- "INTERNAL ERROR: 'never' matches host");
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- sdb_store_matcher_t *m;
-
- if (! strcmp(golden_data[i].op, "OR"))
- m = sdb_store_dis_matcher(golden_data[i].left,
- golden_data[i].right);
- else if (! strcmp(golden_data[i].op, "AND"))
- m = sdb_store_con_matcher(golden_data[i].left,
- golden_data[i].right);
- else {
- fail("INTERNAL ERROR: unexpected operator %s", golden_data[i].op);
- continue;
- }
-
-#define TO_NAME(v) (((v) == always) ? "always" \
- : ((v) == never) ? "never" : "<unknown>")
-
- status = sdb_store_matcher_matches(m, obj);
- fail_unless(status == golden_data[i].expected,
- "%s(%s, %s) = %d; expected: %d", golden_data[i].op,
- TO_NAME(golden_data[i].left), TO_NAME(golden_data[i].right),
- status, golden_data[i].expected);
-
-#undef TO_NAME
-
- sdb_object_deref(SDB_OBJ(m));
- }
-
- sdb_object_deref(SDB_OBJ(always));
- sdb_object_deref(SDB_OBJ(never));
-
- sdb_object_deref(SDB_OBJ(obj));
-}
-END_TEST
-
-START_TEST(test_parse_cmp)
-{
- sdb_store_matcher_t *check;
-
- size_t i;
-
- struct {
- const char *obj_type;
- const char *attr;
- const char *op;
- const char *value;
- int expected;
- } golden_data[] = {
- { "host", "name", "=", "hostname", MATCHER_HOST },
- { "host", "name", "!=", "hostname", MATCHER_NOT },
- { "host", "name", "=~", "hostname", MATCHER_HOST },
- { "host", "name", "!~", "hostname", MATCHER_NOT },
- { "host", "attr", "=", "hostname", -1 },
- { "host", "attr", "!=", "hostname", -1 },
- { "host", "name", "&^", "hostname", -1 },
- { "service", "name", "=", "srvname", MATCHER_HOST },
- { "service", "name", "!=", "srvname", MATCHER_NOT },
- { "service", "name", "=~", "srvname", MATCHER_HOST },
- { "service", "name", "!~", "srvname", MATCHER_NOT },
- { "service", "attr", "=", "srvname", -1 },
- { "service", "attr", "!=", "srvname", -1 },
- { "service", "name", "&^", "srvname", -1 },
- { "attribute", "name", "=", "attrname", MATCHER_HOST },
- { "attribute", "name", "!=", "attrname", MATCHER_NOT },
- { "attribute", "name", "=~", "attrname", MATCHER_HOST },
- { "attribute", "name", "!~", "attrname", MATCHER_NOT },
- { "attribute", "attr", "=", "attrname", MATCHER_HOST },
- { "attribute", "attr", "!=", "attrname", MATCHER_NOT },
- { "attribute", "attr", "=~", "attrname", MATCHER_HOST },
- { "attribute", "attr", "!~", "attrname", MATCHER_NOT },
- { "attribute", "attr", "&^", "attrname", -1 },
- };
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- check = sdb_store_matcher_parse_cmp(golden_data[i].obj_type,
- golden_data[i].attr, golden_data[i].op, golden_data[i].value);
-
- if (golden_data[i].expected == -1) {
- fail_unless(check == NULL,
- "sdb_store_matcher_parse_cmp(%s, %s, %s, %s) = %p; "
- "expected: NULL", golden_data[i].obj_type,
- golden_data[i].attr, golden_data[i].op,
- golden_data[i].value, check);
- continue;
- }
-
- fail_unless(check != NULL,
- "sdb_store_matcher_parse_cmp(%s, %s, %s, %s) = %p; "
- "expected: NULL", golden_data[i].obj_type,
- golden_data[i].attr, golden_data[i].op,
- golden_data[i].value, check);
- fail_unless(M(check)->type == golden_data[i].expected,
- "sdb_store_matcher_parse_cmp(%s, %s, %s, %s) returned matcher "
- "of type %d; expected: %d", golden_data[i].obj_type,
- golden_data[i].attr, golden_data[i].op, golden_data[i].value,
- M(check)->type, golden_data[i].expected);
-
- sdb_object_deref(SDB_OBJ(check));
- }
-}
-END_TEST
-
-static int
-lookup_cb(sdb_store_base_t *obj, void *user_data)
-{
- int *i = user_data;
-
- fail_unless(obj != NULL,
- "sdb_store_lookup callback received NULL obj; expected: "
- "<store base obj>");
- fail_unless(i != NULL,
- "sdb_store_lookup callback received NULL user_data; "
- "expected: <pointer to data>");
-
- ++(*i);
- return 0;
-} /* lookup_cb */
-
-START_TEST(test_lookup)
-{
-#define PTR_RE "0x[0-9a-f]+"
- struct {
- const char *query;
- int expected;
- const char *tostring_re;
- } golden_data[] = {
- { "host.name = 'a'", 1,
- "HOST\\{ NAME\\{ 'a', \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{\\} \\}" },
- { "host.name =~ 'a|b'", 2,
- "HOST\\{ NAME\\{ NULL, "PTR_RE" \\}, SERVICE\\{\\}, ATTR\\{\\} \\}" },
- { "host.name =~ 'host'", 0,
- "HOST\\{ NAME\\{ NULL, "PTR_RE" \\}, SERVICE\\{\\}, ATTR\\{\\} \\}" },
- { "host.name =~ '.'", 3,
- "HOST\\{ NAME\\{ NULL, "PTR_RE" \\}, SERVICE\\{\\}, ATTR\\{\\} \\}" },
- { "service.name = 's1'", 2,
- "HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{ "
- "NAME\\{ 's1', \\(nil\\) }, ATTR\\{\\} "
- "\\}, ATTR\\{\\} \\}" },
- { "service.name =~ 's'", 2,
- "HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{ "
- "NAME\\{ NULL, "PTR_RE" }, ATTR\\{\\} "
- "\\}, ATTR\\{\\} \\}" },
- { "service.name !~ 's'", 1,
- "(NOT, HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{ "
- "NAME\\{ NULL, "PTR_RE" }, ATTR\\{\\} "
- "\\}, ATTR\\{\\} \\})" },
- { "attribute.name = 'k1'", 1,
- "HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{ "
- "NAME\\{ 'k1', \\(nil\\) }, VALUE\\{ NULL, \\(nil\\) \\} "
- "\\} \\}" },
- { "attribute.name = 'x'", 0,
- "HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{ "
- "NAME\\{ 'x', \\(nil\\) }, VALUE\\{ NULL, \\(nil\\) \\} "
- "\\} \\}" },
- { "attribute.k1 = 'v1'", 1,
- "HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{ "
- "NAME\\{ 'k1', \\(nil\\) }, VALUE\\{ 'v1', \\(nil\\) \\} "
- "\\} \\}" },
- { "attribute.k1 != 'v1'", 2,
- "(NOT, HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{ "
- "NAME\\{ 'k1', \\(nil\\) }, VALUE\\{ 'v1', \\(nil\\) \\} "
- "\\} \\})" },
- { "attribute.k1 != 'v2'", 3,
- "(NOT, HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{ "
- "NAME\\{ 'k1', \\(nil\\) }, VALUE\\{ 'v2', \\(nil\\) \\} "
- "\\} \\})" },
- { "attribute.name != 'x' "
- "AND attribute.y !~ 'x'", 3,
- "\\(AND, \\(NOT, HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{ "
- "NAME\\{ 'x', \\(nil\\) }, VALUE\\{ NULL, \\(nil\\) \\} "
- "\\} \\}\\), \\(NOT, HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{ "
- "NAME\\{ 'y', \\(nil\\) }, VALUE\\{ NULL, "PTR_RE" \\} "
- "\\} \\}\\)\\)" },
- };
-
- int check, n;
- size_t i;
-
- n = 0;
- check = sdb_store_lookup(NULL, lookup_cb, &n);
- fail_unless(check == 0,
- "sdb_store_lookup() = %d; expected: 0", check);
- fail_unless(n == 3,
- "sdb_store_lookup called callback %d times; expected: 3", (int)n);
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- sdb_store_matcher_t *m;
- char buf[4096];
-
- m = sdb_fe_parse_matcher(golden_data[i].query, -1);
- fail_unless(m != NULL,
- "sdb_fe_parse_matcher(%s, -1) = NULL; expected: <matcher>",
- golden_data[i].query);
- fail_unless(sdb_regmatches(golden_data[i].tostring_re,
- sdb_store_matcher_tostring(m, buf, sizeof(buf))) == 0,
- "sdb_fe_parse_matcher(%s, -1) = %s; expected: %s",
- golden_data[i].query,
- sdb_store_matcher_tostring(m, buf, sizeof(buf)),
- golden_data[i].tostring_re);
-
- n = 0;
- sdb_store_lookup(m, lookup_cb, &n);
- fail_unless(n == golden_data[i].expected,
- "sdb_store_lookup(matcher{%s}) found %d hosts; expected: %d",
- golden_data[i].query, n, golden_data[i].expected);
- sdb_object_deref(SDB_OBJ(m));
- }
-}
-END_TEST
-
-Suite *
-core_store_lookup_suite(void)
-{
- Suite *s = suite_create("core::store_lookup");
- TCase *tc;
-
- tc = tcase_create("core");
- tcase_add_checked_fixture(tc, populate, sdb_store_clear);
- tcase_add_test(tc, test_store_match);
- tcase_add_test(tc, test_store_match_op);
- tcase_add_test(tc, test_parse_cmp);
- tcase_add_test(tc, test_lookup);
- suite_add_tcase(s, tc);
-
- return s;
-} /* core_store_lookup_suite */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-
diff --git a/t/core/store_test.c b/t/core/store_test.c
--- a/t/core/store_test.c
+++ /dev/null
@@ -1,509 +0,0 @@
-/*
- * 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 "core/store-private.h"
-#include "libsysdb_test.h"
-
-#include <check.h>
-#include <string.h>
-
-static void
-populate(void)
-{
- sdb_data_t datum;
-
- sdb_store_host("h1", 1);
- sdb_store_host("h2", 1);
-
- datum.type = SDB_TYPE_STRING;
- datum.data.string = "v1";
- sdb_store_attribute("h1", "k1", &datum, 1);
- datum.data.string = "v2";
- sdb_store_attribute("h1", "k2", &datum, 1);
- datum.data.string = "v3";
- sdb_store_attribute("h1", "k3", &datum, 1);
-
- sdb_store_service("h2", "s1", 1);
- sdb_store_service("h2", "s2", 1);
-} /* populate */
-
-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_get_host)
-{
- char *golden_hosts[] = { "a", "b", "c" };
- char *unknown_hosts[] = { "x", "y", "z" };
- size_t i;
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_hosts); ++i) {
- int status = sdb_store_host(golden_hosts[i], 1);
- fail_unless(status >= 0,
- "sdb_store_host(%s) = %d; expected: >=0",
- golden_hosts[i], status);
- }
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_hosts); ++i) {
- sdb_store_base_t *sobj1, *sobj2;
- int ref_cnt;
-
- fail_unless(sdb_store_has_host(golden_hosts[i]),
- "sdb_store_has_host(%s) = FALSE; expected: TRUE",
- golden_hosts[i]);
-
- sobj1 = sdb_store_get_host(golden_hosts[i]);
- fail_unless(sobj1 != NULL,
- "sdb_store_get_host(%s) = NULL; expected: <host>",
- golden_hosts[i]);
- ref_cnt = SDB_OBJ(sobj1)->ref_cnt;
-
- fail_unless(ref_cnt > 1,
- "sdb_store_get_host(%s) did not increment ref count: "
- "got: %d; expected: >1", golden_hosts[i], ref_cnt);
-
- sobj2 = sdb_store_get_host(golden_hosts[i]);
- fail_unless(sobj2 != NULL,
- "sdb_store_get_host(%s) = NULL; expected: <host>",
- golden_hosts[i]);
-
- fail_unless(sobj1 == sobj2,
- "sdb_store_get_host(%s) returned different objects "
- "in successive calls", golden_hosts[i]);
- fail_unless(SDB_OBJ(sobj2)->ref_cnt == ref_cnt + 1,
- "sdb_store_get_hosts(%s) did not increment ref count "
- "(first call: %d; second call: %d)",
- golden_hosts[i], ref_cnt, SDB_OBJ(sobj2)->ref_cnt);
-
- sdb_object_deref(SDB_OBJ(sobj1));
- sdb_object_deref(SDB_OBJ(sobj2));
- }
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(unknown_hosts); ++i) {
- sdb_store_base_t *sobj;
-
- fail_unless(!sdb_store_has_host(unknown_hosts[i]),
- "sdb_store_has_host(%s) = TRUE; expected: FALSE",
- unknown_hosts[i]);
-
- sobj = sdb_store_get_host(unknown_hosts[i]);
- fail_unless(!sobj, "sdb_store_get_host(%s) = <host:%s>; expected: NULL",
- unknown_hosts[i], sobj ? SDB_OBJ(sobj)->name : "NULL");
- }
-}
-END_TEST
-
-START_TEST(test_store_attr)
-{
- struct {
- const char *host;
- const char *key;
- 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) {
- sdb_data_t datum;
- int status;
-
- /* XXX: test other types as well */
- datum.type = SDB_TYPE_STRING;
- datum.data.string = golden_data[i].value;
-
- status = sdb_store_attribute(golden_data[i].host,
- golden_data[i].key, &datum,
- 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_service(%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
-
-static void
-verify_json_output(sdb_strbuf_t *buf, const char *expected, int flags)
-{
- int pos;
- size_t len1, len2;
- size_t i;
-
- 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(%x) returned unexpected result\n"
- " got: %s\n %*s\n expected: %s",
- flags, sdb_strbuf_string(buf), pos + 1, "^", expected);
-} /* verify_json_output */
-
-START_TEST(test_store_tojson)
-{
- sdb_strbuf_t *buf;
- size_t i;
-
- struct {
- int flags;
- const char *expected;
- } golden_data[] = {
- { 0, "{\"hosts\":["
- "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
- "\"update_interval\": \"0s\", "
- "\"attributes\": ["
- "{\"name\": \"k1\", \"value\": \"v1\", "
- "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
- "\"update_interval\": \"0s\"},"
- "{\"name\": \"k2\", \"value\": \"v2\", "
- "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
- "\"update_interval\": \"0s\"},"
- "{\"name\": \"k3\", \"value\": \"v3\", "
- "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
- "\"update_interval\": \"0s\"}"
- "], "
- "\"services\": []},"
- "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
- "\"update_interval\": \"0s\", "
- "\"attributes\": [], "
- "\"services\": ["
- "{\"name\": \"s1\", "
- "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
- "\"update_interval\": \"0s\"},"
- "{\"name\": \"s2\", "
- "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
- "\"update_interval\": \"0s\"}"
- "]}"
- "]}" },
- { SDB_SKIP_SERVICES,
- "{\"hosts\":["
- "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
- "\"update_interval\": \"0s\", "
- "\"attributes\": ["
- "{\"name\": \"k1\", \"value\": \"v1\", "
- "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
- "\"update_interval\": \"0s\"},"
- "{\"name\": \"k2\", \"value\": \"v2\", "
- "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
- "\"update_interval\": \"0s\"},"
- "{\"name\": \"k3\", \"value\": \"v3\", "
- "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
- "\"update_interval\": \"0s\"}"
- "]},"
- "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
- "\"update_interval\": \"0s\", "
- "\"attributes\": []}"
- "]}" },
- { SDB_SKIP_ATTRIBUTES,
- "{\"hosts\":["
- "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
- "\"update_interval\": \"0s\", "
- "\"services\": []},"
- "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
- "\"update_interval\": \"0s\", "
- "\"services\": ["
- "{\"name\": \"s1\", "
- "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
- "\"update_interval\": \"0s\"},"
- "{\"name\": \"s2\", "
- "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
- "\"update_interval\": \"0s\"}"
- "]}"
- "]}" },
- { SDB_SKIP_SERVICES | SDB_SKIP_ATTRIBUTES,
- "{\"hosts\":["
- "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
- "\"update_interval\": \"0s\"},"
- "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
- "\"update_interval\": \"0s\"}"
- "]}" },
- };
-
- buf = sdb_strbuf_create(0);
- fail_unless(buf != NULL, "INTERNAL ERROR: failed to create string buffer");
- populate();
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- int status;
-
- sdb_strbuf_clear(buf);
-
- status = sdb_store_tojson(buf, golden_data[i].flags);
- fail_unless(status == 0,
- "sdb_store_tojson(%x) = %d; expected: 0",
- golden_data[i].flags, status);
-
- verify_json_output(buf, golden_data[i].expected, golden_data[i].flags);
- }
- sdb_strbuf_destroy(buf);
-}
-END_TEST
-
-START_TEST(test_interval)
-{
- sdb_store_base_t *host;
-
- /* 10 us interval */
- sdb_store_host("host", 10);
- sdb_store_host("host", 20);
- sdb_store_host("host", 30);
- sdb_store_host("host", 40);
-
- host = sdb_store_get_host("host");
- fail_unless(host != NULL,
- "INTERNAL ERROR: store doesn't have host after adding it");
-
- fail_unless(host->interval == 10,
- "sdb_store_host() did not calculate interval correctly: "
- "got: %"PRIscTIME"; expected: %"PRIscTIME, host->interval, 10);
-
- /* multiple updates for the same timestamp don't modify the interval */
- sdb_store_host("host", 40);
- sdb_store_host("host", 40);
- sdb_store_host("host", 40);
- sdb_store_host("host", 40);
-
- fail_unless(host->interval == 10,
- "sdb_store_host() changed interval when doing multiple updates "
- "using the same timestamp; got: %"PRIscTIME"; "
- "expected: %"PRIscTIME, host->interval, 10);
-
- /* multiple updates using an timestamp don't modify the interval */
- sdb_store_host("host", 20);
- sdb_store_host("host", 20);
- sdb_store_host("host", 20);
- sdb_store_host("host", 20);
-
- fail_unless(host->interval == 10,
- "sdb_store_host() changed interval when doing multiple updates "
- "using an old timestamp; got: %"PRIscTIME"; expected: %"PRIscTIME,
- host->interval, 10);
-
- /* new interval: 20 us */
- sdb_store_host("host", 60);
- fail_unless(host->interval == 11,
- "sdb_store_host() did not calculate interval correctly: "
- "got: %"PRIscTIME"; expected: %"PRIscTIME, host->interval, 11);
-
- /* new interval: 40 us */
- sdb_store_host("host", 100);
- fail_unless(host->interval == 13,
- "sdb_store_host() did not calculate interval correctly: "
- "got: %"PRIscTIME"; expected: %"PRIscTIME, host->interval, 11);
-
- sdb_object_deref(SDB_OBJ(host));
-}
-END_TEST
-
-static int
-iter_incr(sdb_store_base_t *obj, void *user_data)
-{
- intptr_t *i = user_data;
-
- fail_unless(obj != NULL,
- "sdb_store_iterate callback received NULL obj; expected: "
- "<store base obj>");
- fail_unless(i != NULL,
- "sdb_store_iterate callback received NULL user_data; "
- "expected: <pointer to data>");
-
- ++(*i);
- return 0;
-} /* iter_incr */
-
-static int
-iter_error(sdb_store_base_t *obj, void *user_data)
-{
- intptr_t *i = user_data;
-
- fail_unless(obj != NULL,
- "sdb_store_iterate callback received NULL obj; expected: "
- "<store base obj>");
- fail_unless(i != NULL,
- "sdb_store_iterate callback received NULL user_data; "
- "expected: <pointer to data>");
-
- ++(*i);
- return -1;
-} /* iter_error */
-
-START_TEST(test_iterate)
-{
- intptr_t i = 0;
- int check;
-
- /* empty store */
- check = sdb_store_iterate(iter_incr, &i);
- fail_unless(check == -1,
- "sdb_store_iterate(), empty store = %d; expected: -1", check);
- fail_unless(i == 0,
- "sdb_store_iterate called callback %d times; expected: 0", (int)i);
-
- populate();
-
- check = sdb_store_iterate(iter_incr, &i);
- fail_unless(check == 0,
- "sdb_store_iterate() = %d; expected: 0", check);
- fail_unless(i == 2,
- "sdb_store_iterate called callback %d times; expected: 1", (int)i);
-
- i = 0;
- check = sdb_store_iterate(iter_error, &i);
- fail_unless(check == -1,
- "sdb_store_iterate(), error callback = %d; expected: -1", check);
- fail_unless(i == 1,
- "sdb_store_iterate called callback %d times "
- "(callback returned error); expected: 1", (int)i);
-}
-END_TEST
-
-Suite *
-core_store_suite(void)
-{
- Suite *s = suite_create("core::store");
- TCase *tc;
-
- tc = tcase_create("core");
- tcase_add_test(tc, test_store_tojson);
- tcase_add_test(tc, test_store_host);
- tcase_add_test(tc, test_store_get_host);
- tcase_add_test(tc, test_store_attr);
- tcase_add_test(tc, test_store_service);
- tcase_add_test(tc, test_interval);
- tcase_add_test(tc, test_iterate);
- tcase_add_unchecked_fixture(tc, NULL, sdb_store_clear);
- suite_add_tcase(s, tc);
-
- return s;
-} /* core_store_suite */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-
diff --git a/t/core/time_test.c b/t/core/time_test.c
--- a/t/core/time_test.c
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * SysDB - t/core/time_test.c
- * Copyright (C) 2014 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/time.h"
-#include "libsysdb_test.h"
-
-#include <check.h>
-
-START_TEST(test_strfinterval)
-{
- char buf[1024];
- size_t check;
- size_t i;
-
- struct {
- sdb_time_t interval;
- const char *expected;
- } golden_data[] = {
- { 0, "0s" },
- { 4711, ".000004711s" },
- { 1000123400, "1.0001234s" },
- { 47940228000000000L, "1Y6M7D" },
- { SDB_INTERVAL_YEAR, "1Y" },
- { SDB_INTERVAL_MONTH, "1M" },
- { SDB_INTERVAL_DAY, "1D" },
- { SDB_INTERVAL_HOUR, "1h" },
- { SDB_INTERVAL_MINUTE, "1m" },
- { SDB_INTERVAL_SECOND, "1s" },
- { SDB_INTERVAL_YEAR
- + SDB_INTERVAL_MONTH
- + SDB_INTERVAL_DAY
- + SDB_INTERVAL_HOUR
- + SDB_INTERVAL_MINUTE
- + SDB_INTERVAL_SECOND
- + 1234, "1Y1M1D1h1m1.000001234s" },
- };
-
- /* this should return the number of bytes which would have been written;
- * most notably, it should not segfault ;-) */
- check = sdb_strfinterval(NULL, 0, 4711); /* expected: .000004711s */
- fail_unless(check == 11,
- "sdb_strfinterval(NULL, 0, 4711) = %zu; expected: 11", check);
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- check = sdb_strfinterval(buf, sizeof(buf), golden_data[i].interval);
- fail_unless(check > 0,
- "sdb_strfinterval(<buf>, <size>, %"PRIscTIME") = %zu; "
- "expected: >0", golden_data[i].interval, check);
- fail_unless(!strcmp(buf, golden_data[i].expected),
- "sdb_strfinterval(<buf>, <size>, %"PRIscTIME") did not "
- "format interval correctly; got: '%s'; expected: '%s'",
- golden_data[i].interval, buf, golden_data[i].expected);
- fail_unless(check == strlen(golden_data[i].expected),
- "sdb_strfinterval(<buf>, <size>, %"PRIscTIME") = %zu; "
- "expected: %zu", golden_data[i].interval, check,
- strlen(golden_data[i].expected));
- }
-}
-END_TEST
-
-Suite *
-core_time_suite(void)
-{
- Suite *s = suite_create("core::time");
- TCase *tc;
-
- tc = tcase_create("core");
- tcase_add_test(tc, test_strfinterval);
- suite_add_tcase(s, tc);
-
- return s;
-} /* core_time_suite */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-
diff --git a/t/frontend/connection_test.c b/t/frontend/connection_test.c
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * SysDB - t/frontend/connection_test.c
- * Copyright (C) 2014 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.
- */
-
-#if HAVE_CONFIG_H
-# include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include "frontend/connection.h"
-#include "frontend/connection-private.h"
-#include "libsysdb_test.h"
-
-#include "utils/strbuf.h"
-
-#include <check.h>
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <pthread.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-
-/*
- * private helper functions
- */
-
-static void
-mock_conn_destroy(sdb_conn_t *conn)
-{
- if (SDB_OBJ(conn)->name)
- free(SDB_OBJ(conn)->name);
- sdb_strbuf_destroy(conn->buf);
- sdb_strbuf_destroy(conn->errbuf);
- if (conn->fd >= 0)
- close(conn->fd);
- free(conn);
-} /* mock_conn_destroy */
-
-static sdb_conn_t *
-mock_conn_create(void)
-{
- sdb_conn_t *conn;
-
- char tmp_file[] = "connection_test_socket.XXXXXX";
-
- conn = calloc(1, sizeof(*conn));
- if (! conn) {
- fail("INTERNAL ERROR: failed to allocate connection object");
- return NULL;
- }
-
- SDB_OBJ(conn)->name = strdup("mock_connection");
- SDB_OBJ(conn)->ref_cnt = 1;
-
- conn->buf = sdb_strbuf_create(0);
- conn->errbuf = sdb_strbuf_create(0);
- if ((! conn->buf) || (! conn->errbuf)) {
- mock_conn_destroy(conn);
- fail("INTERNAL ERROR: failed to allocate connection object");
- return NULL;
- }
-
- conn->fd = mkstemp(tmp_file);
- if (conn->fd < 0) {
- mock_conn_destroy(conn);
- fail("INTERNAL ERROR: failed to allocate connection object");
- return NULL;
- }
-
- unlink(tmp_file);
-
- conn->cmd = CONNECTION_IDLE;
- conn->cmd_len = 0;
- return conn;
-} /* mock_conn_create */
-
-static void
-mock_conn_rewind(sdb_conn_t *conn)
-{
- lseek(conn->fd, 0, SEEK_SET);
-} /* mock_conn_rewind */
-
-static void
-mock_conn_truncate(sdb_conn_t *conn)
-{
- lseek(conn->fd, 0, SEEK_SET);
- ftruncate(conn->fd, 0);
-} /* mock_conn_truncate */
-
-static int
-mock_unixsock_listener(char *sock_path)
-{
- struct sockaddr_un sa;
- char *filename;
- int fd, status;
-
- filename = tmpnam(sock_path);
- fail_unless(filename != NULL,
- "INTERNAL ERROR: tmpnam() = NULL; expected: a string");
-
- fd = socket(AF_UNIX, SOCK_STREAM, 0);
- fail_unless(fd >= 0,
- "INTERNAL ERROR: socket() = %d; expected: >=0", fd);
-
- memset(&sa, 0, sizeof(sa));
- sa.sun_family = AF_UNIX;
- strncpy(sa.sun_path, filename, sizeof(sa.sun_path));
-
- status = bind(fd, (struct sockaddr *)&sa, sizeof(sa));
- fail_unless(status == 0,
- "INTERNAL ERROR: bind() = %d; expected: 0", status);
- status = listen(fd, 32);
- fail_unless(status == 0,
- "INTERNAL ERROR: listen() = %d; expected: 0", status);
-
- return fd;
-} /* mock_unixsock */
-
-static void *
-mock_client(void *arg)
-{
- char *socket_path = arg;
-
- struct sockaddr_un sa;
- int fd, check;
-
- fd = socket(AF_UNIX, SOCK_STREAM, /* protocol = */ 0);
- fail_unless(fd >= 0,
- "INTERNAL ERROR: socket() = %d; expected: >= 0", fd);
-
- memset(&sa, 0, sizeof(sa));
- sa.sun_family = AF_UNIX;
- strncpy(sa.sun_path, socket_path, sizeof(sa.sun_path));
-
- check = connect(fd, (struct sockaddr *)&sa, sizeof(sa));
- fail_unless(check == 0,
- "INTERNAL ERROR: connect() = %d; expected: 0", check);
-
- close(fd);
- return NULL;
-} /* mock_client */
-
-/*
- * tests
- */
-
-START_TEST(test_conn_accept)
-{
- char socket_path[L_tmpnam];
- int fd, check;
-
- sdb_conn_t *conn;
-
- pthread_t thr;
-
- conn = sdb_connection_accept(-1);
- fail_unless(conn == NULL,
- "sdb_connection_accept(-1) = %p; expected: NULL", conn);
-
- memset(&socket_path, 0, sizeof(socket_path));
- fd = mock_unixsock_listener(socket_path);
- check = pthread_create(&thr, /* attr = */ NULL, mock_client, socket_path);
- fail_unless(check == 0,
- "INTERNAL ERROR: pthread_create() = %i; expected: 0", check);
-
- conn = sdb_connection_accept(fd);
- fail_unless(conn != NULL,
- "sdb_connection_accept(%d) = %p; expected: <conn>", fd, conn);
-
- unlink(socket_path);
- sdb_connection_close(conn);
- pthread_join(thr, NULL);
-}
-END_TEST
-
-START_TEST(test_conn_setup)
-{
- sdb_conn_t *conn = mock_conn_create();
-
- struct {
- uint32_t code;
- const char *msg;
- const char *err;
- } golden_data[] = {
- { UINT32_MAX, NULL, NULL },
- { CONNECTION_IDLE, "fakedata", NULL },
- { CONNECTION_PING, NULL, "Authentication required" },
- { CONNECTION_STARTUP, "fakeuser", NULL },
- { CONNECTION_PING, NULL, NULL },
- { CONNECTION_IDLE, NULL, NULL },
- { CONNECTION_PING, "fakedata", NULL },
- { CONNECTION_IDLE, NULL, NULL },
- };
-
- size_t i;
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- ssize_t check, expected = 0;
-
- mock_conn_truncate(conn);
-
- if (golden_data[i].code != UINT32_MAX) {
- expected = 2 * sizeof(uint32_t)
- + (golden_data[i].msg ? strlen(golden_data[i].msg) : 0);
-
- check = sdb_connection_send(conn, golden_data[i].code,
- (uint32_t)(golden_data[i].msg
- ? strlen(golden_data[i].msg) : 0),
- golden_data[i].msg);
- fail_unless(check == expected,
- "sdb_connection_send(%d, %s) = %zi; expected: %zi",
- golden_data[i].code,
- golden_data[i].msg ? golden_data[i].msg : "<null>",
- check, expected);
- }
-
- mock_conn_rewind(conn);
- check = sdb_connection_read(conn);
- fail_unless(check == expected,
- "sdb_connection_read() = %zi; expected: %zi",
- check, expected);
-
- fail_unless(sdb_strbuf_len(conn->buf) == 0,
- "sdb_connection_read() left %zu bytes in the buffer; "
- "expected: 0", sdb_strbuf_len(conn->buf));
-
- if (golden_data[i].err) {
- const char *err = sdb_strbuf_string(conn->errbuf);
- fail_unless(strcmp(err, golden_data[i].err) == 0,
- "sdb_connection_read(): got error '%s'; "
- "expected: '%s'", err, golden_data[i].err);
- }
- else
- fail_unless(sdb_strbuf_len(conn->errbuf) == 0,
- "sdb_connection_read() left %zu bytes in the error "
- "buffer; expected: 0", sdb_strbuf_len(conn->errbuf));
- }
-
- mock_conn_destroy(conn);
-}
-END_TEST
-
-Suite *
-fe_conn_suite(void)
-{
- Suite *s = suite_create("frontend::connection");
- TCase *tc;
-
- tc = tcase_create("core");
- tcase_add_test(tc, test_conn_accept);
- tcase_add_test(tc, test_conn_setup);
- suite_add_tcase(s, tc);
-
- return s;
-} /* fe_conn_suite */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-
diff --git a/t/frontend/parser_test.c b/t/frontend/parser_test.c
--- a/t/frontend/parser_test.c
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * SysDB - t/frontend/parser_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 "frontend/connection.h"
-#include "frontend/parser.h"
-#include "core/store-private.h"
-#include "core/object.h"
-#include "libsysdb_test.h"
-
-#include <check.h>
-
-/*
- * tests
- */
-
-START_TEST(test_parse)
-{
- struct {
- const char *query;
- int len;
- int expected;
- sdb_conn_state_t expected_cmd;
- } golden_data[] = {
- /* empty commands */
- { NULL, -1, -1, 0 },
- { "", -1, 0, 0 },
- { ";", -1, 0, 0 },
- { ";;", -1, 0, 0 },
-
- /* valid commands */
- { "FETCH 'host'", -1, 1, CONNECTION_FETCH },
- { "LIST", -1, 1, CONNECTION_LIST },
- { "LIST -- comment", -1, 1, CONNECTION_LIST },
- { "LIST;", -1, 1, CONNECTION_LIST },
- { "LIST; INVALID", 5, 1, CONNECTION_LIST },
-
- { "LOOKUP hosts WHERE "
- "host.name = 'host'", -1, 1, CONNECTION_LOOKUP },
- { "LOOKUP hosts WHERE NOT "
- "host.name = 'host'", -1, 1, CONNECTION_LOOKUP },
- { "LOOKUP hosts WHERE "
- "host.name =~ 'p' AND "
- "service.name =~ 'p'", -1, 1, CONNECTION_LOOKUP },
- { "LOOKUP hosts WHERE NOT "
- "host.name =~ 'p' AND "
- "service.name =~ 'p'", -1, 1, CONNECTION_LOOKUP },
- { "LOOKUP hosts WHERE "
- "host.name =~ 'p' AND "
- "service.name =~ 'p' OR "
- "service.name =~ 'r'", -1, 1, CONNECTION_LOOKUP },
- { "LOOKUP hosts WHERE NOT "
- "host.name =~ 'p' AND "
- "service.name =~ 'p' OR "
- "service.name =~ 'r'", -1, 1, CONNECTION_LOOKUP },
-
- /* comments */
- { "/* some comment */", -1, 0, 0 },
- { "-- another comment", -1, 0, 0 },
-
- /* syntax errors */
- { "INVALID", -1, -1, 0 },
- { "FETCH host", -1, -1, 0 },
- { "LIST; INVALID", 8, -1, 0 },
- { "/* some incomplete", -1, -1, 0 },
-
- { "LOOKUP hosts", -1, -1, 0 },
- { "LOOKUP foo WHERE "
- "host.name = 'host'", -1, -1, 0 },
- };
-
- size_t i;
- sdb_llist_t *check;
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- sdb_object_t *obj;
- _Bool ok;
-
- check = sdb_fe_parse(golden_data[i].query, golden_data[i].len);
- if (golden_data[i].expected < 0)
- ok = check == 0;
- else
- ok = sdb_llist_len(check) == (size_t)golden_data[i].expected;
-
- fail_unless(ok, "sdb_fe_parse(%s) = %p (len: %zu); expected: %d",
- golden_data[i].query, check, sdb_llist_len(check),
- golden_data[i].expected);
-
- if (! check)
- continue;
-
- if ((! golden_data[i].expected_cmd)
- || (golden_data[i].expected <= 0)) {
- sdb_llist_destroy(check);
- continue;
- }
-
- obj = sdb_llist_get(check, 0);
- fail_unless(SDB_CONN_NODE(obj)->cmd == golden_data[i].expected_cmd,
- "sdb_fe_parse(%s)->cmd = %i; expected: %d",
- golden_data[i].query, SDB_CONN_NODE(obj)->cmd,
- golden_data[i].expected_cmd);
- sdb_object_deref(obj);
- sdb_llist_destroy(check);
- }
-}
-END_TEST
-
-START_TEST(test_parse_matcher)
-{
- struct {
- const char *expr;
- int len;
- int expected;
- } golden_data[] = {
- /* empty expressions */
- { NULL, -1, -1 },
- { "", -1, -1 },
-
- /* valid expressions */
- { "host.name = 'localhost'", -1, MATCHER_HOST },
- { "host.name != 'localhost'", -1, MATCHER_NOT },
- { "host.name =~ 'host'", -1, MATCHER_HOST },
- { "host.name !~ 'host'", -1, MATCHER_NOT },
- { "host.name = 'localhost' -- foo", -1, MATCHER_HOST },
- { "host.name = 'host' <garbage>", 18, MATCHER_HOST },
- /* match hosts by service */
- { "service.name = 'name'", -1, MATCHER_HOST },
- { "service.name != 'name'", -1, MATCHER_NOT },
- { "service.name =~ 'pattern'", -1, MATCHER_HOST },
- { "service.name !~ 'pattern'", -1, MATCHER_NOT },
- /* match hosts by attribute */
- { "attribute.name = 'name'", -1, MATCHER_HOST },
- { "attribute.name != 'name'", -1, MATCHER_NOT },
- { "attribute.name =~ 'pattern'", -1, MATCHER_HOST },
- { "attribute.name !~ 'pattern'", -1, MATCHER_NOT },
- /* composite expressions */
- { "host.name =~ 'pattern' AND "
- "service.name =~ 'pattern'", -1, MATCHER_AND },
- { "host.name =~ 'pattern' OR "
- "service.name =~ 'pattern'", -1, MATCHER_OR },
- { "NOT host.name = 'host'", -1, MATCHER_NOT },
-
- /* check operator precedence */
- { "host.name = 'name' OR "
- "service.name = 'name' AND "
- "attribute.name = 'name' OR "
- "attribute.foo = 'bar'", -1, MATCHER_OR },
- { "host.name = 'name' AND "
- "service.name = 'name' AND "
- "attribute.name = 'name' OR "
- "attribute.foo = 'bar'", -1, MATCHER_OR },
- { "host.name = 'name' AND "
- "service.name = 'name' OR "
- "attribute.name = 'name' AND "
- "attribute.foo = 'bar'", -1, MATCHER_OR },
- { "(host.name = 'name' OR "
- "service.name = 'name') AND "
- "(attribute.name = 'name' OR "
- "attribute.foo = 'bar')", -1, MATCHER_AND },
- { "NOT host.name = 'name' OR "
- "service.name = 'name'", -1, MATCHER_OR },
- { "NOT host.name = 'name' OR "
- "NOT service.name = 'name'", -1, MATCHER_OR },
- { "NOT (host.name = 'name' OR "
- "NOT service.name = 'name')", -1, MATCHER_NOT },
-
- /* syntax errors */
- { "LIST", -1, -1 },
- { "foo &^ bar", -1, -1 },
- };
-
- size_t i;
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- sdb_store_matcher_t *m;
- m = sdb_fe_parse_matcher(golden_data[i].expr, golden_data[i].len);
-
- if (golden_data[i].expected < 0) {
- fail_unless(m == NULL,
- "sdb_fe_parse_matcher(%s) = %p; expected: NULL",
- golden_data[i].expr, m);
- continue;
- }
-
- fail_unless(m != NULL, "sdb_fe_parse_matcher(%s) = NULL; "
- "expected: <matcher>", golden_data[i].expr);
- fail_unless(M(m)->type == golden_data[i].expected,
- "sdb_fe_parse_matcher(%s) returned matcher of type %d; "
- "expected: %d", golden_data[i].expr, M(m)->type,
- golden_data[i].expected);
-
- sdb_object_deref(SDB_OBJ(m));
- }
-}
-END_TEST
-
-Suite *
-fe_parser_suite(void)
-{
- Suite *s = suite_create("frontend::parser");
- TCase *tc;
-
- tc = tcase_create("core");
- tcase_add_test(tc, test_parse);
- tcase_add_test(tc, test_parse_matcher);
- suite_add_tcase(s, tc);
-
- return s;
-} /* util_parser_suite */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-
diff --git a/t/frontend/sock_test.c b/t/frontend/sock_test.c
--- a/t/frontend/sock_test.c
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * SysDB - t/frontend/sock_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 "frontend/sock.h"
-#include "libsysdb_test.h"
-
-#include <check.h>
-
-#include <errno.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <unistd.h>
-
-#include <pthread.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-
-/*
- * private variables
- */
-
-static sdb_fe_socket_t *sock;
-
-static void
-setup(void)
-{
- sock = sdb_fe_sock_create();
- fail_unless(sock != NULL,
- "sdb_fe_sock_create() = NULL; expected frontend sock object");
-} /* setup */
-
-static void
-teardown(void)
-{
- sdb_fe_sock_destroy(sock);
- sock = NULL;
-} /* teardown */
-
-static void
-sock_listen(char *tmp_file)
-{
- char sock_addr[strlen("unix:") + L_tmpnam + 1];
- char *filename;
-
- int check;
-
- filename = tmpnam(tmp_file);
- fail_unless(filename != NULL,
- "INTERNAL ERROR: tmpnam() = NULL; expected: a string");
-
- sprintf(sock_addr, "unix:%s", tmp_file);
- check = sdb_fe_sock_add_listener(sock, sock_addr);
- fail_unless(check == 0,
- "sdb_fe_sock_add_listener(%s) = %i; expected: 0",
- sock_addr, check);
-} /* conn */
-
-/*
- * parallel testing
- */
-
-static void *
-sock_handler(void *data)
-{
- sdb_fe_loop_t *loop = data;
- int check;
-
- check = sdb_fe_sock_listen_and_serve(sock, loop);
- fail_unless(check == 0,
- "sdb_fe_sock_listen_and_serve() = %i; "
- "expected: 0 (after adding listener)", check);
- return NULL;
-} /* sock_handler */
-
-/*
- * tests
- */
-
-START_TEST(test_listen_and_serve)
-{
- sdb_fe_loop_t loop = SDB_FE_LOOP_INIT;
-
- char tmp_file[L_tmpnam];
- int check;
-
- pthread_t thr;
-
- int sock_fd;
- struct sockaddr_un sa;
-
- check = sdb_fe_sock_listen_and_serve(sock, &loop);
- fail_unless(check < 0,
- "sdb_fe_sock_listen_and_serve() = %i; "
- "expected: <0 (before adding listeners)", check);
-
- sock_listen(tmp_file);
-
- loop.do_loop = 1;
- check = pthread_create(&thr, /* attr = */ NULL, sock_handler, &loop);
- fail_unless(check == 0,
- "INTERNAL ERROR: pthread_create() = %i; expected: 0", check);
-
- sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
- fail_unless(sock_fd >= 0,
- "INTERNAL ERROR: socket() = %d; expected: >= 0", sock_fd);
-
- sa.sun_family = AF_UNIX;
- strncpy(sa.sun_path, tmp_file, sizeof(sa.sun_path));
-
- /* wait for socket to become available */
- errno = ECONNREFUSED;
- while (errno == ECONNREFUSED) {
- check = connect(sock_fd, (struct sockaddr *)&sa, sizeof(sa));
- if (! check)
- break;
-
- fail_unless(errno == ECONNREFUSED,
- "INTERNAL ERROR: connect() = %d [errno=%d]; expected: 0",
- check, errno);
- }
-
- close(sock_fd);
-
- loop.do_loop = 0;
- pthread_join(thr, NULL);
-
- fail_unless(access(tmp_file, F_OK),
- "sdb_fe_sock_listen_and_serve() did not clean up "
- "socket %s", tmp_file);
-
- /* should do nothing and not report errors */
- check = sdb_fe_sock_listen_and_serve(sock, &loop);
- fail_unless(check == 0,
- "sdb_fe_sock_listen_and_serve() = %i; "
- "expected: <0 (do_loop == 0)", check);
- fail_unless(access(tmp_file, F_OK),
- "sdb_fe_sock_listen_and_serve() recreated socket "
- "(do_loop == 0)");
-}
-END_TEST
-
-Suite *
-fe_sock_suite(void)
-{
- Suite *s = suite_create("frontend::sock");
- TCase *tc;
-
- tc = tcase_create("core");
- tcase_add_checked_fixture(tc, setup, teardown);
- tcase_add_test(tc, test_listen_and_serve);
- suite_add_tcase(s, tc);
-
- return s;
-} /* util_unixsock_suite */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-
diff --git a/t/libsysdb_net_test.c b/t/libsysdb_net_test.c
--- a/t/libsysdb_net_test.c
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * SysDB - t/libsysdb_net_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.
- */
-
-/*
- * testing component involving networking operations
- */
-
-#if HAVE_CONFIG_H
-# include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include "libsysdb_test.h"
-
-#include <check.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-int
-main(void)
-{
- int failed = 0;
- size_t i;
-
- suite_creator_t creators[] = {
-#ifdef HAVE_FOPENCOOKIE
- { util_unixsock_suite, NULL },
-#else
- { NULL, "Skipping util::unixsock; missing fopencookie" },
-#endif /* HAVE_FOPENCOOKIE */
- };
-
- putenv("TZ=UTC");
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(creators); ++i) {
- SRunner *sr;
- Suite *s;
-
- if (creators[i].msg)
- printf("%s\n", creators[i].msg);
-
- if (!creators[i].creator)
- continue;
-
- s = creators[i].creator();
- sr = srunner_create(s);
- srunner_run_all(sr, CK_NORMAL);
- failed += srunner_ntests_failed(sr);
- srunner_free(sr);
- }
-
- return failed;
-} /* main */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-
diff --git a/t/libsysdb_test.c b/t/libsysdb_test.c
--- a/t/libsysdb_test.c
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * SysDB - t/libsysdb_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.
- */
-
-#if HAVE_CONFIG_H
-# include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include "libsysdb_test.h"
-
-#include <check.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-int
-main(void)
-{
- int failed = 0;
- size_t i;
-
- suite_creator_t creators[] = {
- { core_data_suite, NULL },
- { core_object_suite, NULL },
- { core_store_suite, NULL },
- { core_store_lookup_suite, NULL },
- { core_time_suite, NULL },
- { fe_conn_suite, NULL },
- { fe_parser_suite, NULL },
- { fe_sock_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;
-
- if (creators[i].msg)
- printf("%s\n", creators[i].msg);
-
- if (!creators[i].creator)
- continue;
-
- s = creators[i].creator();
- sr = srunner_create(s);
- srunner_run_all(sr, CK_NORMAL);
- failed += srunner_ntests_failed(sr);
- srunner_free(sr);
- }
-
- return failed;
-} /* main */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-
diff --git a/t/libsysdb_test.h b/t/libsysdb_test.h
--- a/t/libsysdb_test.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * SysDB - t/libsysdb_test.h
- * 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.
- */
-
-#ifndef T_LIBSYSDB_H
-#define T_LIBSYSDB_H 1
-
-#include "sysdb.h"
-#include "core/object.h"
-
-#include "libsysdb_testutils.h"
-
-#include <check.h>
-#include <string.h>
-
-/*
- * private testing helpers
- */
-
-/* static string object:
- * Any such object may is of type sdb_object_t but may never be destroyed. */
-#define SSTRING_OBJ(name) { \
- /* type = */ { sizeof(sdb_object_t), NULL, NULL }, \
- /* ref_cnt = */ 1, /* name = */ (name) }
-
-/*
- * test-related data-types
- */
-
-typedef struct {
- Suite *(*creator)(void);
- const char *msg;
-} suite_creator_t;
-
-/*
- * test suites
- */
-
-/* t/core/data_test */
-Suite *
-core_data_suite(void);
-
-/* t/core/object_test */
-Suite *
-core_object_suite(void);
-
-/* t/core/store_test */
-Suite *
-core_store_suite(void);
-
-/* t/core/store_lookup_test */
-Suite *
-core_store_lookup_suite(void);
-
-/* t/core/time_test */
-Suite *
-core_time_suite(void);
-
-/* t/frontend/connection_test */
-Suite *
-fe_conn_suite(void);
-
-/* t/frontend/parser_test */
-Suite *
-fe_parser_suite(void);
-
-/* t/frontend/sock_test */
-Suite *
-fe_sock_suite(void);
-
-/* t/utils/channel_test */
-Suite *
-util_channel_suite(void);
-
-/* t/utils/dbi_test */
-Suite *
-util_dbi_suite(void);
-
-/* t/utils/llist_test */
-Suite *
-util_llist_suite(void);
-
-/* t/utils/strbuf_test */
-Suite *
-util_strbuf_suite(void);
-
-/* t/utils/unixsock_test */
-Suite *
-util_unixsock_suite(void);
-
-#endif /* T_LIBSYSDB_H */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-
diff --git a/t/libsysdb_testutils.c b/t/libsysdb_testutils.c
--- a/t/libsysdb_testutils.c
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * SysDB - t/libsysdb_testutils.c
- * Copyright (C) 2014 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.
- */
-
-#if HAVE_CONFIG_H
-# include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include "libsysdb_testutils.h"
-
-#include <stdlib.h>
-#include <sys/types.h>
-#include <regex.h>
-
-int
-sdb_regmatches(const char *regex, const char *string)
-{
- regex_t reg;
- int status;
-
- status = regcomp(®, regex, REG_EXTENDED | REG_NOSUB);
- if (status)
- return status;
-
- status = regexec(®, string, /* matches = */ 0, NULL, /* flags = */ 0);
- regfree(®);
- return status;
-} /* sdb_regmatches */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-
diff --git a/t/libsysdb_testutils.h b/t/libsysdb_testutils.h
--- a/t/libsysdb_testutils.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * SysDB - t/libsysdb_testutils.h
- * Copyright (C) 2014 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.
- */
-
-/*
- * Utility functions for test suites.
- */
-
-#ifndef T_LIBSYSDB_UTILS_H
-#define T_LIBSYSDB_UTILS_H 1
-
-/*
- * sdb_regmatches:
- * Check if a regex matches a string.
- *
- * Returns:
- * - 0 if the regex matches
- * - a non-zero error value else (see regcomp(3) for details)
- */
-int
-sdb_regmatches(const char *regex, const char *string);
-
-#endif /* T_LIBSYSDB_UTILS_H */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-
diff --git a/t/unit/core/data_test.c b/t/unit/core/data_test.c
--- /dev/null
+++ b/t/unit/core/data_test.c
@@ -0,0 +1,201 @@
+/*
+ * SysDB - t/unit/core/data_test.c
+ * Copyright (C) 2014 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/data.h"
+#include "libsysdb_test.h"
+
+#include <check.h>
+
+START_TEST(test_data)
+{
+ sdb_data_t d1, d2;
+ int check;
+
+ d2.type = SDB_TYPE_INTEGER;
+ d2.data.integer = 4711;
+ check = sdb_data_copy(&d1, &d2);
+ fail_unless(!check, "sdb_data_copy() = %i; expected: 0", check);
+ fail_unless(d1.type == d2.type,
+ "sdb_data_copy() didn't copy type; got: %i; expected: %i",
+ d1.type, d2.type);
+ fail_unless(d1.data.integer == d2.data.integer,
+ "sdb_data_copy() didn't copy integer data: got: %d; expected: %d",
+ d1.data.integer, d2.data.integer);
+
+ d2.type = SDB_TYPE_DECIMAL;
+ d2.data.decimal = 47.11;
+ check = sdb_data_copy(&d1, &d2);
+ fail_unless(!check, "sdb_data_copy() = %i; expected: 0", check);
+ fail_unless(d1.type == d2.type,
+ "sdb_data_copy() didn't copy type; got: %i; expected: %i",
+ d1.type, d2.type);
+ fail_unless(d1.data.decimal == d2.data.decimal,
+ "sdb_data_copy() didn't copy decimal data: got: %f; expected: %f",
+ d1.data.decimal, d2.data.decimal);
+
+ d2.type = SDB_TYPE_STRING;
+ d2.data.string = "some string";
+ check = sdb_data_copy(&d1, &d2);
+ fail_unless(!check, "sdb_data_copy() = %i; expected: 0", check);
+ fail_unless(d1.type == d2.type,
+ "sdb_data_copy() didn't copy type; got: %i; expected: %i",
+ d1.type, d2.type);
+ fail_unless(!strcmp(d1.data.string, d2.data.string),
+ "sdb_data_copy() didn't copy string data: got: %s; expected: %s",
+ d1.data.string, d2.data.string);
+
+ sdb_data_free_datum(&d1);
+ fail_unless(d1.data.string == NULL,
+ "sdb_data_free_datum() didn't free string data");
+
+ d2.type = SDB_TYPE_DATETIME;
+ d2.data.datetime = 4711;
+ check = sdb_data_copy(&d1, &d2);
+ fail_unless(!check, "sdb_data_copy() = %i; expected: 0", check);
+ fail_unless(d1.type == d2.type,
+ "sdb_data_copy() didn't copy type; got: %i; expected: %i",
+ d1.type, d2.type);
+ fail_unless(d1.data.datetime == d2.data.datetime,
+ "sdb_data_copy() didn't copy datetime data: got: %d; expected: %d",
+ d1.data.datetime, d2.data.datetime);
+
+ d2.type = SDB_TYPE_BINARY;
+ d2.data.binary.datum = (unsigned char *)"some string";
+ d2.data.binary.length = strlen((const char *)d2.data.binary.datum);
+ check = sdb_data_copy(&d1, &d2);
+ fail_unless(!check, "sdb_data_copy() = %i; expected: 0", check);
+ fail_unless(d1.type == d2.type,
+ "sdb_data_copy() didn't copy type; got: %i; expected: %i",
+ d1.type, d2.type);
+ fail_unless(d1.data.binary.length == d2.data.binary.length,
+ "sdb_data_copy() didn't copy length; got: %d; expected: 5d",
+ d1.data.binary.length, d2.data.binary.length);
+ fail_unless(!memcmp(d1.data.binary.datum, d2.data.binary.datum,
+ d2.data.binary.length),
+ "sdb_data_copy() didn't copy binary data: got: %s; expected: %s",
+ d1.data.string, d2.data.string);
+
+ sdb_data_free_datum(&d1);
+ fail_unless(d1.data.binary.length == 0,
+ "sdb_data_free_datum() didn't reset binary datum length");
+ fail_unless(d1.data.binary.datum == NULL,
+ "sdb_data_free_datum() didn't free binary datum");
+}
+END_TEST
+
+START_TEST(test_format)
+{
+ struct {
+ sdb_data_t datum;
+ const char *expected;
+ } golden_data[] = {
+ {
+ { SDB_TYPE_INTEGER, { .integer = 4711 } },
+ "4711",
+ },
+ {
+ { SDB_TYPE_DECIMAL, { .decimal = 65536.0 } },
+ "0x1p+16",
+ },
+ {
+ { SDB_TYPE_STRING, { .string = NULL } },
+ "\"NULL\"",
+ },
+ {
+ { SDB_TYPE_STRING, { .string = "this is a test" } },
+ "\"this is a test\"",
+ },
+ {
+ { SDB_TYPE_STRING, { .string = "special \\ \" characters" } },
+ "\"special \\\\ \\\" characters\"",
+ },
+ {
+ { SDB_TYPE_DATETIME, { .datetime= 471147114711471100 } },
+ "\"1984-12-06 02:11:54 +0000\"",
+ },
+ {
+ { SDB_TYPE_BINARY, { .binary = { 0, NULL } } },
+ "\"\"",
+ },
+ {
+ {
+ SDB_TYPE_BINARY,
+ { .binary = { 12, (unsigned char *)"binary\0crap\x42" } },
+ },
+ "\"\\x62\\x69\\x6e\\x61\\x72\\x79\\x0\\x63\\x72\\x61\\x70\\x42\"",
+ },
+ };
+
+ size_t i;
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ sdb_data_t *datum = &golden_data[i].datum;
+ char buf[sdb_data_strlen(datum) + 2];
+ int check;
+
+ memset(buf, (int)'A', sizeof(buf));
+
+ check = sdb_data_format(datum, buf, sizeof(buf) - 1,
+ SDB_DOUBLE_QUOTED);
+ fail_unless(check > 0,
+ "sdb_data_format(type=%s) = %d; expected: >0",
+ SDB_TYPE_TO_STRING(datum->type), check);
+ fail_unless(! strcmp(buf, golden_data[i].expected),
+ "sdb_data_format(type=%s) used wrong format: %s; expected: %s",
+ SDB_TYPE_TO_STRING(datum->type), buf, golden_data[i].expected);
+
+ fail_unless((size_t)check <= sizeof(buf) - 2,
+ "sdb_data_format(type=%s) wrote %d bytes; "
+ "expected <= %zu based on sdb_data_strlen()",
+ SDB_TYPE_TO_STRING(datum->type), check, sizeof(buf) - 2);
+
+ fail_unless(buf[sizeof(buf) - 2] == '\0',
+ "sdb_data_format(type=%s) did not nul-terminate the buffer",
+ SDB_TYPE_TO_STRING(datum->type));
+ fail_unless(buf[sizeof(buf) - 1] == 'A',
+ "sdb_data_format(type=%s) wrote past the end of the buffer",
+ SDB_TYPE_TO_STRING(datum->type));
+ }
+}
+END_TEST
+
+Suite *
+core_data_suite(void)
+{
+ Suite *s = suite_create("core::data");
+ TCase *tc;
+
+ tc = tcase_create("core");
+ tcase_add_test(tc, test_data);
+ tcase_add_test(tc, test_format);
+ suite_add_tcase(s, tc);
+
+ return s;
+} /* core_data_suite */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/t/unit/core/object_test.c b/t/unit/core/object_test.c
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * SysDB - t/unit/core/object_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/object.h"
+#include "libsysdb_test.h"
+
+#include <check.h>
+
+/*
+ * private data types
+ */
+
+static int init_noop_called = 0;
+static int init_noop_retval = 0;
+static int
+obj_init_noop(sdb_object_t *obj, va_list __attribute__((unused)) ap)
+{
+ ++init_noop_called;
+ fail_unless(obj != NULL, "obj init function: received obj == NULL");
+ return init_noop_retval;
+} /* obj_init_noop */
+
+static int destroy_noop_called = 0;
+static void
+obj_destroy_noop(sdb_object_t *obj)
+{
+ ++destroy_noop_called;
+ fail_unless(obj != NULL, "obj destroy function: received obj == NULL");
+} /* obj_destroy_noop */
+
+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)
+{
+ ++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, test_type);
+ fail_unless(obj != NULL,
+ "sdb_object_create() = NULL; expected: a new object");
+ fail_unless(obj->type.size == test_type.size,
+ "after sdb_object_create(): type size mismatch; got: %zu; "
+ "expected: %zu", obj->type.size, test_type.size);
+ fail_unless(obj->type.init == obj_init_noop,
+ "after sdb_object_create(): type init = %p; exptected: %p",
+ obj->type.init, obj_init_noop);
+ fail_unless(obj->type.destroy == obj_destroy_noop,
+ "after sdb_object_create(): type destroy = %p; exptected: %p",
+ obj->type.destroy, obj_destroy_noop);
+ fail_unless(obj->ref_cnt == 1,
+ "after sdb_object_create(): obj->ref_cnt = %d; expected: 1",
+ obj->ref_cnt);
+ fail_unless(!strcmp(obj->name, "test-object"),
+ "after sdb_object_create(): obj->name = '%s'; expected: '%s'",
+ obj->name, name);
+ fail_unless(obj->name != name,
+ "after sdb_object_create(): obj->name was not strdup()'ed");
+
+ fail_unless(init_noop_called == 1,
+ "sdb_object_create() did not call object's init function");
+ fail_unless(destroy_noop_called == 0,
+ "sdb_object_create() called object's destroy function");
+ fail_unless(((struct noop *)obj)->data == 0,
+ "sdb_object_create() did not initialize data to zero");
+
+ sdb_object_deref(obj);
+ fail_unless(destroy_noop_called == 1,
+ "sdb_object_deref() did not call object's destroy function");
+
+ init_noop_called = 0;
+ init_noop_retval = -1;
+ destroy_noop_called = 0;
+ obj = sdb_object_create(name, test_type);
+ fail_unless(obj == NULL,
+ "sdb_object_create() = %p; expected NULL (init returned -1)",
+ obj);
+ fail_unless(init_noop_called == 1,
+ "sdb_object_create() did not call object's init function");
+ fail_unless(destroy_noop_called == 1,
+ "sdb_object_create() did not call object's destroy function "
+ "after init failure");
+
+ test_type.size = 1;
+ init_noop_called = 0;
+ init_noop_retval = 0;
+ destroy_noop_called = 0;
+ obj = sdb_object_create(name, test_type);
+ fail_unless(obj == NULL,
+ "sdb_object_create() = %p; expected NULL (type's size too small)",
+ obj);
+ fail_unless(init_noop_called == 0,
+ "sdb_object_create() called object's init function "
+ "when size was too small");
+ fail_unless(destroy_noop_called == 0,
+ "sdb_object_create() called object's destroy function "
+ "when size was too small");
+
+ test_type.size = sizeof(struct noop);
+ init_noop_retval = 0;
+ 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);
+
+ 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);
+
+ init_noop_called = 0;
+ obj = sdb_object_create_simple(name, sizeof(struct noop), NULL);
+ fail_unless(obj != NULL,
+ "sdb_object_create_simple() = NULL; expected: <obj>");
+ fail_unless(obj->type.size == sizeof(struct noop),
+ "sdb_object_create_simple() created object of size %zu; "
+ "expected: %zu", obj->type.size, sizeof(struct noop));
+ fail_unless(obj->type.init == NULL,
+ "sdb_object_create_simple() did not set init=NULL");
+ fail_unless(obj->type.destroy == NULL,
+ "sdb_object_create_simple() did not set destroy=NULL");
+ fail_unless(init_noop_called == 0,
+ "sdb_object_create_simple() unexpectedly called noop's init");
+ sdb_object_deref(obj);
+
+ obj = sdb_object_create_T(NULL, struct noop);
+ fail_unless(obj != NULL,
+ "sdb_object_create_simple() = NULL; expected: <obj>");
+ fail_unless(obj->type.size == sizeof(struct noop),
+ "sdb_object_create_simple() created object of size %zu; "
+ "expected: %zu", obj->type.size, sizeof(struct noop));
+ fail_unless(obj->type.init == NULL,
+ "sdb_object_create_simple() did not set init=NULL");
+ fail_unless(obj->type.destroy == NULL,
+ "sdb_object_create_simple() did not set destroy=NULL");
+ fail_unless(init_noop_called == 0,
+ "sdb_object_create_simple() unexpectedly called noop's init");
+ 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(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)
+{
+ Suite *s = suite_create("core::object");
+ TCase *tc;
+
+ 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;
+} /* core_object_suite */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/t/unit/core/store_lookup_test.c b/t/unit/core/store_lookup_test.c
--- /dev/null
@@ -0,0 +1,535 @@
+/*
+ * SysDB - t/unit/core/store_lookup_test.c
+ * Copyright (C) 2014 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 "core/store-private.h"
+#include "frontend/parser.h"
+#include "libsysdb_test.h"
+
+#include <check.h>
+#include <string.h>
+
+static void
+populate(void)
+{
+ const char *hosts[] = { "a", "b", "c" };
+
+ struct {
+ const char *host;
+ const char *service;
+ } services[] = {
+ { "a", "s1" },
+ { "a", "s2" },
+ { "b", "s1" },
+ { "b", "s3" },
+ };
+
+ struct {
+ const char *host;
+ const char *name;
+ sdb_data_t value;
+ } attrs[] = {
+ { "a", "k1", { SDB_TYPE_STRING, { .string = "v1" } } },
+ };
+
+ size_t i;
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(hosts); ++i) {
+ int status = sdb_store_host(hosts[i], 1);
+ fail_unless(status == 0,
+ "sdb_store_host(%s, 1) = %d; expected: 0",
+ hosts[i], status);
+ }
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(services); ++i) {
+ int status = sdb_store_service(services[i].host,
+ services[i].service, 1);
+ fail_unless(status == 0,
+ "sdb_store_service(%s, %s, 1) = %d; expected: 0",
+ services[i].host, services[i].service, status);
+ }
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(attrs); ++i) {
+ int status = sdb_store_attribute(attrs[i].host,
+ attrs[i].name, &attrs[i].value, 1);
+ fail_unless(status == 0,
+ "sdb_store_attribute(%s, %s, <val>, 1) = %d; expected: 0",
+ attrs[i].host, attrs[i].name, status);
+ }
+} /* populate */
+
+START_TEST(test_store_match)
+{
+ sdb_store_base_t *obj;
+
+ struct {
+ const char *hostname;
+ const char *hostname_re;
+
+ const char *service_name;
+ const char *service_name_re;
+
+ const char *attr_name;
+ const char *attr_name_re;
+ const char *attr_value;
+ const char *attr_value_re;
+
+ int expected;
+ } golden_data[] = {
+ {
+ /* host */ NULL, NULL,
+ /* svc */ NULL, NULL,
+ /* attr */ NULL, NULL, NULL, NULL, 1
+ },
+ {
+ /* host */ "a", NULL,
+ /* svc */ NULL, NULL,
+ /* attr */ NULL, NULL, NULL, NULL, 1
+ },
+ {
+ /* host */ "b", NULL,
+ /* svc */ NULL, NULL,
+ /* attr */ NULL, NULL, NULL, NULL, 0
+ },
+ {
+ /* host */ NULL, "^a$",
+ /* svc */ NULL, NULL,
+ /* attr */ NULL, NULL, NULL, NULL, 1
+ },
+ {
+ /* host */ NULL, "^b$",
+ /* svc */ NULL, NULL,
+ /* attr */ NULL, NULL, NULL, NULL, 0
+ },
+ {
+ /* host */ "a", "^a$",
+ /* svc */ NULL, NULL,
+ /* attr */ NULL, NULL, NULL, NULL, 1
+ },
+ {
+ /* host */ "a", "^b$",
+ /* svc */ NULL, NULL,
+ /* attr */ NULL, NULL, NULL, NULL, 0
+ },
+ {
+ /* host */ "b", "^a$",
+ /* svc */ NULL, NULL,
+ /* attr */ NULL, NULL, NULL, NULL, 0
+ },
+ {
+ /* host */ "a", "^a$",
+ /* svc */ "s1", NULL,
+ /* attr */ NULL, NULL, NULL, NULL, 1
+ },
+ {
+ /* host */ "a", "^a$",
+ /* svc */ NULL, "^s1$",
+ /* attr */ NULL, NULL, NULL, NULL, 1
+ },
+ {
+ /* host */ "a", "^a$",
+ /* svc */ "s1", "^s1$",
+ /* attr */ NULL, NULL, NULL, NULL, 1
+ },
+ {
+ /* host */ "a", "^a$",
+ /* svc */ "x1", NULL,
+ /* attr */ NULL, NULL, NULL, NULL, 0
+ },
+ {
+ /* host */ "a", "^a$",
+ /* svc */ NULL, "x",
+ /* attr */ NULL, NULL, NULL, NULL, 0
+ },
+ {
+ /* host */ "a", "^a$",
+ /* svc */ "x1", "x",
+ /* attr */ NULL, NULL, NULL, NULL, 0
+ },
+ {
+ /* host */ "a", "^a$",
+ /* svc */ "s1", "x",
+ /* attr */ NULL, NULL, NULL, NULL, 0
+ },
+ {
+ /* host */ "a", "^a$",
+ /* svc */ "x1", "s",
+ /* attr */ NULL, NULL, NULL, NULL, 0
+ },
+ {
+ /* host */ "a", "^a$",
+ /* svc */ "s1", "^s1$",
+ /* attr */ "k1", NULL, NULL, NULL, 1
+ },
+ {
+ /* host */ "a", "^a$",
+ /* svc */ "s1", "^s1$",
+ /* attr */ NULL, "^k", NULL, NULL, 1
+ },
+ {
+ /* host */ "a", "^a$",
+ /* svc */ "s1", "^s1$",
+ /* attr */ NULL, NULL, "v1", NULL, 1
+ },
+ {
+ /* host */ "a", "^a$",
+ /* svc */ "s1", "^s1$",
+ /* attr */ NULL, NULL, NULL, "^v1$", 1
+ },
+ {
+ /* host */ "a", "^a$",
+ /* svc */ "s1", "^s1$",
+ /* attr */ "k1", "1", "v1", "1", 1
+ },
+ };
+
+ size_t i;
+
+ obj = sdb_store_get_host("a");
+ fail_unless(obj != NULL,
+ "sdb_store_get_host(a) = NULL; expected: <host>");
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ sdb_store_matcher_t *h, *s, *a, *n;
+ int status;
+
+ s = sdb_store_service_matcher(golden_data[i].service_name,
+ golden_data[i].service_name_re, NULL);
+ fail_unless(s != NULL,
+ "sdb_store_service_matcher() = NULL; expected: <matcher>");
+
+ a = sdb_store_attr_matcher(golden_data[i].attr_name,
+ golden_data[i].attr_name_re, golden_data[i].attr_value,
+ golden_data[i].attr_value_re);
+ fail_unless(a != NULL,
+ "sdb_store_attr_matcher() = NULL; expected: <matcher>");
+
+ h = sdb_store_host_matcher(golden_data[i].hostname,
+ golden_data[i].hostname_re, s, a);
+ fail_unless(h != NULL,
+ "sdb_store_host_matcher() = NULL: expected: <matcher>");
+ /* pass ownership to the host matcher */
+ sdb_object_deref(SDB_OBJ(s));
+ sdb_object_deref(SDB_OBJ(a));
+
+ status = sdb_store_matcher_matches(h, obj);
+ fail_unless(status == golden_data[i].expected,
+ "sdb_store_matcher_matches({{%s, %s},{%s, %s},"
+ "{%s, %s, %s, %s}}, <host a>) = %d; expected: %d",
+ golden_data[i].hostname, golden_data[i].hostname_re,
+ golden_data[i].service_name, golden_data[i].service_name_re,
+ golden_data[i].attr_name, golden_data[i].attr_name_re,
+ golden_data[i].attr_value, golden_data[i].attr_value_re,
+ status, golden_data[i].expected);
+
+ n = sdb_store_inv_matcher(h);
+ fail_unless(n != NULL,
+ "sdb_store_inv_matcher() = NULL; expected: <matcher>");
+ sdb_object_deref(SDB_OBJ(h));
+
+ /* now match the inverted set of objects */
+ status = sdb_store_matcher_matches(n, obj);
+ fail_unless(status == !golden_data[i].expected,
+ "sdb_store_matcher_matches(NOT{{%s, %s},{%s, %s},"
+ "{%s, %s, %s, %s}}, <host a>) = %d; expected: %d",
+ golden_data[i].hostname, golden_data[i].hostname_re,
+ golden_data[i].service_name, golden_data[i].service_name_re,
+ golden_data[i].attr_name, golden_data[i].attr_name_re,
+ golden_data[i].attr_value, golden_data[i].attr_value_re,
+ status, !golden_data[i].expected);
+
+ sdb_object_deref(SDB_OBJ(n));
+ }
+
+ sdb_object_deref(SDB_OBJ(obj));
+}
+END_TEST
+
+START_TEST(test_store_match_op)
+{
+ sdb_store_base_t *obj;
+
+ sdb_store_matcher_t *always = sdb_store_host_matcher(NULL, NULL, NULL, NULL);
+ sdb_store_matcher_t *never = sdb_store_host_matcher("a", "b", NULL, NULL);
+
+ struct {
+ const char *op;
+ sdb_store_matcher_t *left;
+ sdb_store_matcher_t *right;
+ int expected;
+ } golden_data[] = {
+ { "OR", always, always, 1 },
+ { "OR", always, never, 1 },
+ { "OR", never, always, 1 },
+ { "OR", never, never, 0 },
+ { "AND", always, always, 1 },
+ { "AND", always, never, 0 },
+ { "AND", never, always, 0 },
+ { "AND", never, never, 0 },
+ };
+
+ int status;
+ size_t i;
+
+ obj = sdb_store_get_host("a");
+
+ status = sdb_store_matcher_matches(always, obj);
+ fail_unless(status == 1,
+ "INTERNAL ERROR: 'always' did not match host");
+ status = sdb_store_matcher_matches(never, obj);
+ fail_unless(status == 0,
+ "INTERNAL ERROR: 'never' matches host");
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ sdb_store_matcher_t *m;
+
+ if (! strcmp(golden_data[i].op, "OR"))
+ m = sdb_store_dis_matcher(golden_data[i].left,
+ golden_data[i].right);
+ else if (! strcmp(golden_data[i].op, "AND"))
+ m = sdb_store_con_matcher(golden_data[i].left,
+ golden_data[i].right);
+ else {
+ fail("INTERNAL ERROR: unexpected operator %s", golden_data[i].op);
+ continue;
+ }
+
+#define TO_NAME(v) (((v) == always) ? "always" \
+ : ((v) == never) ? "never" : "<unknown>")
+
+ status = sdb_store_matcher_matches(m, obj);
+ fail_unless(status == golden_data[i].expected,
+ "%s(%s, %s) = %d; expected: %d", golden_data[i].op,
+ TO_NAME(golden_data[i].left), TO_NAME(golden_data[i].right),
+ status, golden_data[i].expected);
+
+#undef TO_NAME
+
+ sdb_object_deref(SDB_OBJ(m));
+ }
+
+ sdb_object_deref(SDB_OBJ(always));
+ sdb_object_deref(SDB_OBJ(never));
+
+ sdb_object_deref(SDB_OBJ(obj));
+}
+END_TEST
+
+START_TEST(test_parse_cmp)
+{
+ sdb_store_matcher_t *check;
+
+ size_t i;
+
+ struct {
+ const char *obj_type;
+ const char *attr;
+ const char *op;
+ const char *value;
+ int expected;
+ } golden_data[] = {
+ { "host", "name", "=", "hostname", MATCHER_HOST },
+ { "host", "name", "!=", "hostname", MATCHER_NOT },
+ { "host", "name", "=~", "hostname", MATCHER_HOST },
+ { "host", "name", "!~", "hostname", MATCHER_NOT },
+ { "host", "attr", "=", "hostname", -1 },
+ { "host", "attr", "!=", "hostname", -1 },
+ { "host", "name", "&^", "hostname", -1 },
+ { "service", "name", "=", "srvname", MATCHER_HOST },
+ { "service", "name", "!=", "srvname", MATCHER_NOT },
+ { "service", "name", "=~", "srvname", MATCHER_HOST },
+ { "service", "name", "!~", "srvname", MATCHER_NOT },
+ { "service", "attr", "=", "srvname", -1 },
+ { "service", "attr", "!=", "srvname", -1 },
+ { "service", "name", "&^", "srvname", -1 },
+ { "attribute", "name", "=", "attrname", MATCHER_HOST },
+ { "attribute", "name", "!=", "attrname", MATCHER_NOT },
+ { "attribute", "name", "=~", "attrname", MATCHER_HOST },
+ { "attribute", "name", "!~", "attrname", MATCHER_NOT },
+ { "attribute", "attr", "=", "attrname", MATCHER_HOST },
+ { "attribute", "attr", "!=", "attrname", MATCHER_NOT },
+ { "attribute", "attr", "=~", "attrname", MATCHER_HOST },
+ { "attribute", "attr", "!~", "attrname", MATCHER_NOT },
+ { "attribute", "attr", "&^", "attrname", -1 },
+ };
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ check = sdb_store_matcher_parse_cmp(golden_data[i].obj_type,
+ golden_data[i].attr, golden_data[i].op, golden_data[i].value);
+
+ if (golden_data[i].expected == -1) {
+ fail_unless(check == NULL,
+ "sdb_store_matcher_parse_cmp(%s, %s, %s, %s) = %p; "
+ "expected: NULL", golden_data[i].obj_type,
+ golden_data[i].attr, golden_data[i].op,
+ golden_data[i].value, check);
+ continue;
+ }
+
+ fail_unless(check != NULL,
+ "sdb_store_matcher_parse_cmp(%s, %s, %s, %s) = %p; "
+ "expected: NULL", golden_data[i].obj_type,
+ golden_data[i].attr, golden_data[i].op,
+ golden_data[i].value, check);
+ fail_unless(M(check)->type == golden_data[i].expected,
+ "sdb_store_matcher_parse_cmp(%s, %s, %s, %s) returned matcher "
+ "of type %d; expected: %d", golden_data[i].obj_type,
+ golden_data[i].attr, golden_data[i].op, golden_data[i].value,
+ M(check)->type, golden_data[i].expected);
+
+ sdb_object_deref(SDB_OBJ(check));
+ }
+}
+END_TEST
+
+static int
+lookup_cb(sdb_store_base_t *obj, void *user_data)
+{
+ int *i = user_data;
+
+ fail_unless(obj != NULL,
+ "sdb_store_lookup callback received NULL obj; expected: "
+ "<store base obj>");
+ fail_unless(i != NULL,
+ "sdb_store_lookup callback received NULL user_data; "
+ "expected: <pointer to data>");
+
+ ++(*i);
+ return 0;
+} /* lookup_cb */
+
+START_TEST(test_lookup)
+{
+#define PTR_RE "0x[0-9a-f]+"
+ struct {
+ const char *query;
+ int expected;
+ const char *tostring_re;
+ } golden_data[] = {
+ { "host.name = 'a'", 1,
+ "HOST\\{ NAME\\{ 'a', \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{\\} \\}" },
+ { "host.name =~ 'a|b'", 2,
+ "HOST\\{ NAME\\{ NULL, "PTR_RE" \\}, SERVICE\\{\\}, ATTR\\{\\} \\}" },
+ { "host.name =~ 'host'", 0,
+ "HOST\\{ NAME\\{ NULL, "PTR_RE" \\}, SERVICE\\{\\}, ATTR\\{\\} \\}" },
+ { "host.name =~ '.'", 3,
+ "HOST\\{ NAME\\{ NULL, "PTR_RE" \\}, SERVICE\\{\\}, ATTR\\{\\} \\}" },
+ { "service.name = 's1'", 2,
+ "HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{ "
+ "NAME\\{ 's1', \\(nil\\) }, ATTR\\{\\} "
+ "\\}, ATTR\\{\\} \\}" },
+ { "service.name =~ 's'", 2,
+ "HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{ "
+ "NAME\\{ NULL, "PTR_RE" }, ATTR\\{\\} "
+ "\\}, ATTR\\{\\} \\}" },
+ { "service.name !~ 's'", 1,
+ "(NOT, HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{ "
+ "NAME\\{ NULL, "PTR_RE" }, ATTR\\{\\} "
+ "\\}, ATTR\\{\\} \\})" },
+ { "attribute.name = 'k1'", 1,
+ "HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{ "
+ "NAME\\{ 'k1', \\(nil\\) }, VALUE\\{ NULL, \\(nil\\) \\} "
+ "\\} \\}" },
+ { "attribute.name = 'x'", 0,
+ "HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{ "
+ "NAME\\{ 'x', \\(nil\\) }, VALUE\\{ NULL, \\(nil\\) \\} "
+ "\\} \\}" },
+ { "attribute.k1 = 'v1'", 1,
+ "HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{ "
+ "NAME\\{ 'k1', \\(nil\\) }, VALUE\\{ 'v1', \\(nil\\) \\} "
+ "\\} \\}" },
+ { "attribute.k1 != 'v1'", 2,
+ "(NOT, HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{ "
+ "NAME\\{ 'k1', \\(nil\\) }, VALUE\\{ 'v1', \\(nil\\) \\} "
+ "\\} \\})" },
+ { "attribute.k1 != 'v2'", 3,
+ "(NOT, HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{ "
+ "NAME\\{ 'k1', \\(nil\\) }, VALUE\\{ 'v2', \\(nil\\) \\} "
+ "\\} \\})" },
+ { "attribute.name != 'x' "
+ "AND attribute.y !~ 'x'", 3,
+ "\\(AND, \\(NOT, HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{ "
+ "NAME\\{ 'x', \\(nil\\) }, VALUE\\{ NULL, \\(nil\\) \\} "
+ "\\} \\}\\), \\(NOT, HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{ "
+ "NAME\\{ 'y', \\(nil\\) }, VALUE\\{ NULL, "PTR_RE" \\} "
+ "\\} \\}\\)\\)" },
+ };
+
+ int check, n;
+ size_t i;
+
+ n = 0;
+ check = sdb_store_lookup(NULL, lookup_cb, &n);
+ fail_unless(check == 0,
+ "sdb_store_lookup() = %d; expected: 0", check);
+ fail_unless(n == 3,
+ "sdb_store_lookup called callback %d times; expected: 3", (int)n);
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ sdb_store_matcher_t *m;
+ char buf[4096];
+
+ m = sdb_fe_parse_matcher(golden_data[i].query, -1);
+ fail_unless(m != NULL,
+ "sdb_fe_parse_matcher(%s, -1) = NULL; expected: <matcher>",
+ golden_data[i].query);
+ fail_unless(sdb_regmatches(golden_data[i].tostring_re,
+ sdb_store_matcher_tostring(m, buf, sizeof(buf))) == 0,
+ "sdb_fe_parse_matcher(%s, -1) = %s; expected: %s",
+ golden_data[i].query,
+ sdb_store_matcher_tostring(m, buf, sizeof(buf)),
+ golden_data[i].tostring_re);
+
+ n = 0;
+ sdb_store_lookup(m, lookup_cb, &n);
+ fail_unless(n == golden_data[i].expected,
+ "sdb_store_lookup(matcher{%s}) found %d hosts; expected: %d",
+ golden_data[i].query, n, golden_data[i].expected);
+ sdb_object_deref(SDB_OBJ(m));
+ }
+}
+END_TEST
+
+Suite *
+core_store_lookup_suite(void)
+{
+ Suite *s = suite_create("core::store_lookup");
+ TCase *tc;
+
+ tc = tcase_create("core");
+ tcase_add_checked_fixture(tc, populate, sdb_store_clear);
+ tcase_add_test(tc, test_store_match);
+ tcase_add_test(tc, test_store_match_op);
+ tcase_add_test(tc, test_parse_cmp);
+ tcase_add_test(tc, test_lookup);
+ suite_add_tcase(s, tc);
+
+ return s;
+} /* core_store_lookup_suite */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/t/unit/core/store_test.c b/t/unit/core/store_test.c
--- /dev/null
+++ b/t/unit/core/store_test.c
@@ -0,0 +1,509 @@
+/*
+ * SysDB - t/unit/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 "core/store-private.h"
+#include "libsysdb_test.h"
+
+#include <check.h>
+#include <string.h>
+
+static void
+populate(void)
+{
+ sdb_data_t datum;
+
+ sdb_store_host("h1", 1);
+ sdb_store_host("h2", 1);
+
+ datum.type = SDB_TYPE_STRING;
+ datum.data.string = "v1";
+ sdb_store_attribute("h1", "k1", &datum, 1);
+ datum.data.string = "v2";
+ sdb_store_attribute("h1", "k2", &datum, 1);
+ datum.data.string = "v3";
+ sdb_store_attribute("h1", "k3", &datum, 1);
+
+ sdb_store_service("h2", "s1", 1);
+ sdb_store_service("h2", "s2", 1);
+} /* populate */
+
+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_get_host)
+{
+ char *golden_hosts[] = { "a", "b", "c" };
+ char *unknown_hosts[] = { "x", "y", "z" };
+ size_t i;
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_hosts); ++i) {
+ int status = sdb_store_host(golden_hosts[i], 1);
+ fail_unless(status >= 0,
+ "sdb_store_host(%s) = %d; expected: >=0",
+ golden_hosts[i], status);
+ }
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_hosts); ++i) {
+ sdb_store_base_t *sobj1, *sobj2;
+ int ref_cnt;
+
+ fail_unless(sdb_store_has_host(golden_hosts[i]),
+ "sdb_store_has_host(%s) = FALSE; expected: TRUE",
+ golden_hosts[i]);
+
+ sobj1 = sdb_store_get_host(golden_hosts[i]);
+ fail_unless(sobj1 != NULL,
+ "sdb_store_get_host(%s) = NULL; expected: <host>",
+ golden_hosts[i]);
+ ref_cnt = SDB_OBJ(sobj1)->ref_cnt;
+
+ fail_unless(ref_cnt > 1,
+ "sdb_store_get_host(%s) did not increment ref count: "
+ "got: %d; expected: >1", golden_hosts[i], ref_cnt);
+
+ sobj2 = sdb_store_get_host(golden_hosts[i]);
+ fail_unless(sobj2 != NULL,
+ "sdb_store_get_host(%s) = NULL; expected: <host>",
+ golden_hosts[i]);
+
+ fail_unless(sobj1 == sobj2,
+ "sdb_store_get_host(%s) returned different objects "
+ "in successive calls", golden_hosts[i]);
+ fail_unless(SDB_OBJ(sobj2)->ref_cnt == ref_cnt + 1,
+ "sdb_store_get_hosts(%s) did not increment ref count "
+ "(first call: %d; second call: %d)",
+ golden_hosts[i], ref_cnt, SDB_OBJ(sobj2)->ref_cnt);
+
+ sdb_object_deref(SDB_OBJ(sobj1));
+ sdb_object_deref(SDB_OBJ(sobj2));
+ }
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(unknown_hosts); ++i) {
+ sdb_store_base_t *sobj;
+
+ fail_unless(!sdb_store_has_host(unknown_hosts[i]),
+ "sdb_store_has_host(%s) = TRUE; expected: FALSE",
+ unknown_hosts[i]);
+
+ sobj = sdb_store_get_host(unknown_hosts[i]);
+ fail_unless(!sobj, "sdb_store_get_host(%s) = <host:%s>; expected: NULL",
+ unknown_hosts[i], sobj ? SDB_OBJ(sobj)->name : "NULL");
+ }
+}
+END_TEST
+
+START_TEST(test_store_attr)
+{
+ struct {
+ const char *host;
+ const char *key;
+ 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) {
+ sdb_data_t datum;
+ int status;
+
+ /* XXX: test other types as well */
+ datum.type = SDB_TYPE_STRING;
+ datum.data.string = golden_data[i].value;
+
+ status = sdb_store_attribute(golden_data[i].host,
+ golden_data[i].key, &datum,
+ 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_service(%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
+
+static void
+verify_json_output(sdb_strbuf_t *buf, const char *expected, int flags)
+{
+ int pos;
+ size_t len1, len2;
+ size_t i;
+
+ 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(%x) returned unexpected result\n"
+ " got: %s\n %*s\n expected: %s",
+ flags, sdb_strbuf_string(buf), pos + 1, "^", expected);
+} /* verify_json_output */
+
+START_TEST(test_store_tojson)
+{
+ sdb_strbuf_t *buf;
+ size_t i;
+
+ struct {
+ int flags;
+ const char *expected;
+ } golden_data[] = {
+ { 0, "{\"hosts\":["
+ "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
+ "\"update_interval\": \"0s\", "
+ "\"attributes\": ["
+ "{\"name\": \"k1\", \"value\": \"v1\", "
+ "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
+ "\"update_interval\": \"0s\"},"
+ "{\"name\": \"k2\", \"value\": \"v2\", "
+ "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
+ "\"update_interval\": \"0s\"},"
+ "{\"name\": \"k3\", \"value\": \"v3\", "
+ "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
+ "\"update_interval\": \"0s\"}"
+ "], "
+ "\"services\": []},"
+ "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
+ "\"update_interval\": \"0s\", "
+ "\"attributes\": [], "
+ "\"services\": ["
+ "{\"name\": \"s1\", "
+ "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
+ "\"update_interval\": \"0s\"},"
+ "{\"name\": \"s2\", "
+ "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
+ "\"update_interval\": \"0s\"}"
+ "]}"
+ "]}" },
+ { SDB_SKIP_SERVICES,
+ "{\"hosts\":["
+ "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
+ "\"update_interval\": \"0s\", "
+ "\"attributes\": ["
+ "{\"name\": \"k1\", \"value\": \"v1\", "
+ "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
+ "\"update_interval\": \"0s\"},"
+ "{\"name\": \"k2\", \"value\": \"v2\", "
+ "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
+ "\"update_interval\": \"0s\"},"
+ "{\"name\": \"k3\", \"value\": \"v3\", "
+ "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
+ "\"update_interval\": \"0s\"}"
+ "]},"
+ "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
+ "\"update_interval\": \"0s\", "
+ "\"attributes\": []}"
+ "]}" },
+ { SDB_SKIP_ATTRIBUTES,
+ "{\"hosts\":["
+ "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
+ "\"update_interval\": \"0s\", "
+ "\"services\": []},"
+ "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
+ "\"update_interval\": \"0s\", "
+ "\"services\": ["
+ "{\"name\": \"s1\", "
+ "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
+ "\"update_interval\": \"0s\"},"
+ "{\"name\": \"s2\", "
+ "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
+ "\"update_interval\": \"0s\"}"
+ "]}"
+ "]}" },
+ { SDB_SKIP_SERVICES | SDB_SKIP_ATTRIBUTES,
+ "{\"hosts\":["
+ "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
+ "\"update_interval\": \"0s\"},"
+ "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
+ "\"update_interval\": \"0s\"}"
+ "]}" },
+ };
+
+ buf = sdb_strbuf_create(0);
+ fail_unless(buf != NULL, "INTERNAL ERROR: failed to create string buffer");
+ populate();
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ int status;
+
+ sdb_strbuf_clear(buf);
+
+ status = sdb_store_tojson(buf, golden_data[i].flags);
+ fail_unless(status == 0,
+ "sdb_store_tojson(%x) = %d; expected: 0",
+ golden_data[i].flags, status);
+
+ verify_json_output(buf, golden_data[i].expected, golden_data[i].flags);
+ }
+ sdb_strbuf_destroy(buf);
+}
+END_TEST
+
+START_TEST(test_interval)
+{
+ sdb_store_base_t *host;
+
+ /* 10 us interval */
+ sdb_store_host("host", 10);
+ sdb_store_host("host", 20);
+ sdb_store_host("host", 30);
+ sdb_store_host("host", 40);
+
+ host = sdb_store_get_host("host");
+ fail_unless(host != NULL,
+ "INTERNAL ERROR: store doesn't have host after adding it");
+
+ fail_unless(host->interval == 10,
+ "sdb_store_host() did not calculate interval correctly: "
+ "got: %"PRIscTIME"; expected: %"PRIscTIME, host->interval, 10);
+
+ /* multiple updates for the same timestamp don't modify the interval */
+ sdb_store_host("host", 40);
+ sdb_store_host("host", 40);
+ sdb_store_host("host", 40);
+ sdb_store_host("host", 40);
+
+ fail_unless(host->interval == 10,
+ "sdb_store_host() changed interval when doing multiple updates "
+ "using the same timestamp; got: %"PRIscTIME"; "
+ "expected: %"PRIscTIME, host->interval, 10);
+
+ /* multiple updates using an timestamp don't modify the interval */
+ sdb_store_host("host", 20);
+ sdb_store_host("host", 20);
+ sdb_store_host("host", 20);
+ sdb_store_host("host", 20);
+
+ fail_unless(host->interval == 10,
+ "sdb_store_host() changed interval when doing multiple updates "
+ "using an old timestamp; got: %"PRIscTIME"; expected: %"PRIscTIME,
+ host->interval, 10);
+
+ /* new interval: 20 us */
+ sdb_store_host("host", 60);
+ fail_unless(host->interval == 11,
+ "sdb_store_host() did not calculate interval correctly: "
+ "got: %"PRIscTIME"; expected: %"PRIscTIME, host->interval, 11);
+
+ /* new interval: 40 us */
+ sdb_store_host("host", 100);
+ fail_unless(host->interval == 13,
+ "sdb_store_host() did not calculate interval correctly: "
+ "got: %"PRIscTIME"; expected: %"PRIscTIME, host->interval, 11);
+
+ sdb_object_deref(SDB_OBJ(host));
+}
+END_TEST
+
+static int
+iter_incr(sdb_store_base_t *obj, void *user_data)
+{
+ intptr_t *i = user_data;
+
+ fail_unless(obj != NULL,
+ "sdb_store_iterate callback received NULL obj; expected: "
+ "<store base obj>");
+ fail_unless(i != NULL,
+ "sdb_store_iterate callback received NULL user_data; "
+ "expected: <pointer to data>");
+
+ ++(*i);
+ return 0;
+} /* iter_incr */
+
+static int
+iter_error(sdb_store_base_t *obj, void *user_data)
+{
+ intptr_t *i = user_data;
+
+ fail_unless(obj != NULL,
+ "sdb_store_iterate callback received NULL obj; expected: "
+ "<store base obj>");
+ fail_unless(i != NULL,
+ "sdb_store_iterate callback received NULL user_data; "
+ "expected: <pointer to data>");
+
+ ++(*i);
+ return -1;
+} /* iter_error */
+
+START_TEST(test_iterate)
+{
+ intptr_t i = 0;
+ int check;
+
+ /* empty store */
+ check = sdb_store_iterate(iter_incr, &i);
+ fail_unless(check == -1,
+ "sdb_store_iterate(), empty store = %d; expected: -1", check);
+ fail_unless(i == 0,
+ "sdb_store_iterate called callback %d times; expected: 0", (int)i);
+
+ populate();
+
+ check = sdb_store_iterate(iter_incr, &i);
+ fail_unless(check == 0,
+ "sdb_store_iterate() = %d; expected: 0", check);
+ fail_unless(i == 2,
+ "sdb_store_iterate called callback %d times; expected: 1", (int)i);
+
+ i = 0;
+ check = sdb_store_iterate(iter_error, &i);
+ fail_unless(check == -1,
+ "sdb_store_iterate(), error callback = %d; expected: -1", check);
+ fail_unless(i == 1,
+ "sdb_store_iterate called callback %d times "
+ "(callback returned error); expected: 1", (int)i);
+}
+END_TEST
+
+Suite *
+core_store_suite(void)
+{
+ Suite *s = suite_create("core::store");
+ TCase *tc;
+
+ tc = tcase_create("core");
+ tcase_add_test(tc, test_store_tojson);
+ tcase_add_test(tc, test_store_host);
+ tcase_add_test(tc, test_store_get_host);
+ tcase_add_test(tc, test_store_attr);
+ tcase_add_test(tc, test_store_service);
+ tcase_add_test(tc, test_interval);
+ tcase_add_test(tc, test_iterate);
+ tcase_add_unchecked_fixture(tc, NULL, sdb_store_clear);
+ suite_add_tcase(s, tc);
+
+ return s;
+} /* core_store_suite */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/t/unit/core/time_test.c b/t/unit/core/time_test.c
--- /dev/null
+++ b/t/unit/core/time_test.c
@@ -0,0 +1,99 @@
+/*
+ * SysDB - t/unit/core/time_test.c
+ * Copyright (C) 2014 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/time.h"
+#include "libsysdb_test.h"
+
+#include <check.h>
+
+START_TEST(test_strfinterval)
+{
+ char buf[1024];
+ size_t check;
+ size_t i;
+
+ struct {
+ sdb_time_t interval;
+ const char *expected;
+ } golden_data[] = {
+ { 0, "0s" },
+ { 4711, ".000004711s" },
+ { 1000123400, "1.0001234s" },
+ { 47940228000000000L, "1Y6M7D" },
+ { SDB_INTERVAL_YEAR, "1Y" },
+ { SDB_INTERVAL_MONTH, "1M" },
+ { SDB_INTERVAL_DAY, "1D" },
+ { SDB_INTERVAL_HOUR, "1h" },
+ { SDB_INTERVAL_MINUTE, "1m" },
+ { SDB_INTERVAL_SECOND, "1s" },
+ { SDB_INTERVAL_YEAR
+ + SDB_INTERVAL_MONTH
+ + SDB_INTERVAL_DAY
+ + SDB_INTERVAL_HOUR
+ + SDB_INTERVAL_MINUTE
+ + SDB_INTERVAL_SECOND
+ + 1234, "1Y1M1D1h1m1.000001234s" },
+ };
+
+ /* this should return the number of bytes which would have been written;
+ * most notably, it should not segfault ;-) */
+ check = sdb_strfinterval(NULL, 0, 4711); /* expected: .000004711s */
+ fail_unless(check == 11,
+ "sdb_strfinterval(NULL, 0, 4711) = %zu; expected: 11", check);
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ check = sdb_strfinterval(buf, sizeof(buf), golden_data[i].interval);
+ fail_unless(check > 0,
+ "sdb_strfinterval(<buf>, <size>, %"PRIscTIME") = %zu; "
+ "expected: >0", golden_data[i].interval, check);
+ fail_unless(!strcmp(buf, golden_data[i].expected),
+ "sdb_strfinterval(<buf>, <size>, %"PRIscTIME") did not "
+ "format interval correctly; got: '%s'; expected: '%s'",
+ golden_data[i].interval, buf, golden_data[i].expected);
+ fail_unless(check == strlen(golden_data[i].expected),
+ "sdb_strfinterval(<buf>, <size>, %"PRIscTIME") = %zu; "
+ "expected: %zu", golden_data[i].interval, check,
+ strlen(golden_data[i].expected));
+ }
+}
+END_TEST
+
+Suite *
+core_time_suite(void)
+{
+ Suite *s = suite_create("core::time");
+ TCase *tc;
+
+ tc = tcase_create("core");
+ tcase_add_test(tc, test_strfinterval);
+ suite_add_tcase(s, tc);
+
+ return s;
+} /* core_time_suite */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/t/unit/frontend/connection_test.c b/t/unit/frontend/connection_test.c
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * SysDB - t/unit/frontend/connection_test.c
+ * Copyright (C) 2014 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.
+ */
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include "frontend/connection.h"
+#include "frontend/connection-private.h"
+#include "libsysdb_test.h"
+
+#include "utils/strbuf.h"
+
+#include <check.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <pthread.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+/*
+ * private helper functions
+ */
+
+static void
+mock_conn_destroy(sdb_conn_t *conn)
+{
+ if (SDB_OBJ(conn)->name)
+ free(SDB_OBJ(conn)->name);
+ sdb_strbuf_destroy(conn->buf);
+ sdb_strbuf_destroy(conn->errbuf);
+ if (conn->fd >= 0)
+ close(conn->fd);
+ free(conn);
+} /* mock_conn_destroy */
+
+static sdb_conn_t *
+mock_conn_create(void)
+{
+ sdb_conn_t *conn;
+
+ char tmp_file[] = "connection_test_socket.XXXXXX";
+
+ conn = calloc(1, sizeof(*conn));
+ if (! conn) {
+ fail("INTERNAL ERROR: failed to allocate connection object");
+ return NULL;
+ }
+
+ SDB_OBJ(conn)->name = strdup("mock_connection");
+ SDB_OBJ(conn)->ref_cnt = 1;
+
+ conn->buf = sdb_strbuf_create(0);
+ conn->errbuf = sdb_strbuf_create(0);
+ if ((! conn->buf) || (! conn->errbuf)) {
+ mock_conn_destroy(conn);
+ fail("INTERNAL ERROR: failed to allocate connection object");
+ return NULL;
+ }
+
+ conn->fd = mkstemp(tmp_file);
+ if (conn->fd < 0) {
+ mock_conn_destroy(conn);
+ fail("INTERNAL ERROR: failed to allocate connection object");
+ return NULL;
+ }
+
+ unlink(tmp_file);
+
+ conn->cmd = CONNECTION_IDLE;
+ conn->cmd_len = 0;
+ return conn;
+} /* mock_conn_create */
+
+static void
+mock_conn_rewind(sdb_conn_t *conn)
+{
+ lseek(conn->fd, 0, SEEK_SET);
+} /* mock_conn_rewind */
+
+static void
+mock_conn_truncate(sdb_conn_t *conn)
+{
+ lseek(conn->fd, 0, SEEK_SET);
+ ftruncate(conn->fd, 0);
+} /* mock_conn_truncate */
+
+static int
+mock_unixsock_listener(char *sock_path)
+{
+ struct sockaddr_un sa;
+ char *filename;
+ int fd, status;
+
+ filename = tmpnam(sock_path);
+ fail_unless(filename != NULL,
+ "INTERNAL ERROR: tmpnam() = NULL; expected: a string");
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ fail_unless(fd >= 0,
+ "INTERNAL ERROR: socket() = %d; expected: >=0", fd);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sun_family = AF_UNIX;
+ strncpy(sa.sun_path, filename, sizeof(sa.sun_path));
+
+ status = bind(fd, (struct sockaddr *)&sa, sizeof(sa));
+ fail_unless(status == 0,
+ "INTERNAL ERROR: bind() = %d; expected: 0", status);
+ status = listen(fd, 32);
+ fail_unless(status == 0,
+ "INTERNAL ERROR: listen() = %d; expected: 0", status);
+
+ return fd;
+} /* mock_unixsock */
+
+static void *
+mock_client(void *arg)
+{
+ char *socket_path = arg;
+
+ struct sockaddr_un sa;
+ int fd, check;
+
+ fd = socket(AF_UNIX, SOCK_STREAM, /* protocol = */ 0);
+ fail_unless(fd >= 0,
+ "INTERNAL ERROR: socket() = %d; expected: >= 0", fd);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sun_family = AF_UNIX;
+ strncpy(sa.sun_path, socket_path, sizeof(sa.sun_path));
+
+ check = connect(fd, (struct sockaddr *)&sa, sizeof(sa));
+ fail_unless(check == 0,
+ "INTERNAL ERROR: connect() = %d; expected: 0", check);
+
+ close(fd);
+ return NULL;
+} /* mock_client */
+
+/*
+ * tests
+ */
+
+START_TEST(test_conn_accept)
+{
+ char socket_path[L_tmpnam];
+ int fd, check;
+
+ sdb_conn_t *conn;
+
+ pthread_t thr;
+
+ conn = sdb_connection_accept(-1);
+ fail_unless(conn == NULL,
+ "sdb_connection_accept(-1) = %p; expected: NULL", conn);
+
+ memset(&socket_path, 0, sizeof(socket_path));
+ fd = mock_unixsock_listener(socket_path);
+ check = pthread_create(&thr, /* attr = */ NULL, mock_client, socket_path);
+ fail_unless(check == 0,
+ "INTERNAL ERROR: pthread_create() = %i; expected: 0", check);
+
+ conn = sdb_connection_accept(fd);
+ fail_unless(conn != NULL,
+ "sdb_connection_accept(%d) = %p; expected: <conn>", fd, conn);
+
+ unlink(socket_path);
+ sdb_connection_close(conn);
+ pthread_join(thr, NULL);
+}
+END_TEST
+
+START_TEST(test_conn_setup)
+{
+ sdb_conn_t *conn = mock_conn_create();
+
+ struct {
+ uint32_t code;
+ const char *msg;
+ const char *err;
+ } golden_data[] = {
+ { UINT32_MAX, NULL, NULL },
+ { CONNECTION_IDLE, "fakedata", NULL },
+ { CONNECTION_PING, NULL, "Authentication required" },
+ { CONNECTION_STARTUP, "fakeuser", NULL },
+ { CONNECTION_PING, NULL, NULL },
+ { CONNECTION_IDLE, NULL, NULL },
+ { CONNECTION_PING, "fakedata", NULL },
+ { CONNECTION_IDLE, NULL, NULL },
+ };
+
+ size_t i;
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ ssize_t check, expected = 0;
+
+ mock_conn_truncate(conn);
+
+ if (golden_data[i].code != UINT32_MAX) {
+ expected = 2 * sizeof(uint32_t)
+ + (golden_data[i].msg ? strlen(golden_data[i].msg) : 0);
+
+ check = sdb_connection_send(conn, golden_data[i].code,
+ (uint32_t)(golden_data[i].msg
+ ? strlen(golden_data[i].msg) : 0),
+ golden_data[i].msg);
+ fail_unless(check == expected,
+ "sdb_connection_send(%d, %s) = %zi; expected: %zi",
+ golden_data[i].code,
+ golden_data[i].msg ? golden_data[i].msg : "<null>",
+ check, expected);
+ }
+
+ mock_conn_rewind(conn);
+ check = sdb_connection_read(conn);
+ fail_unless(check == expected,
+ "sdb_connection_read() = %zi; expected: %zi",
+ check, expected);
+
+ fail_unless(sdb_strbuf_len(conn->buf) == 0,
+ "sdb_connection_read() left %zu bytes in the buffer; "
+ "expected: 0", sdb_strbuf_len(conn->buf));
+
+ if (golden_data[i].err) {
+ const char *err = sdb_strbuf_string(conn->errbuf);
+ fail_unless(strcmp(err, golden_data[i].err) == 0,
+ "sdb_connection_read(): got error '%s'; "
+ "expected: '%s'", err, golden_data[i].err);
+ }
+ else
+ fail_unless(sdb_strbuf_len(conn->errbuf) == 0,
+ "sdb_connection_read() left %zu bytes in the error "
+ "buffer; expected: 0", sdb_strbuf_len(conn->errbuf));
+ }
+
+ mock_conn_destroy(conn);
+}
+END_TEST
+
+Suite *
+fe_conn_suite(void)
+{
+ Suite *s = suite_create("frontend::connection");
+ TCase *tc;
+
+ tc = tcase_create("core");
+ tcase_add_test(tc, test_conn_accept);
+ tcase_add_test(tc, test_conn_setup);
+ suite_add_tcase(s, tc);
+
+ return s;
+} /* fe_conn_suite */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/t/unit/frontend/parser_test.c b/t/unit/frontend/parser_test.c
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * SysDB - t/unit/frontend/parser_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 "frontend/connection.h"
+#include "frontend/parser.h"
+#include "core/store-private.h"
+#include "core/object.h"
+#include "libsysdb_test.h"
+
+#include <check.h>
+
+/*
+ * tests
+ */
+
+START_TEST(test_parse)
+{
+ struct {
+ const char *query;
+ int len;
+ int expected;
+ sdb_conn_state_t expected_cmd;
+ } golden_data[] = {
+ /* empty commands */
+ { NULL, -1, -1, 0 },
+ { "", -1, 0, 0 },
+ { ";", -1, 0, 0 },
+ { ";;", -1, 0, 0 },
+
+ /* valid commands */
+ { "FETCH 'host'", -1, 1, CONNECTION_FETCH },
+ { "LIST", -1, 1, CONNECTION_LIST },
+ { "LIST -- comment", -1, 1, CONNECTION_LIST },
+ { "LIST;", -1, 1, CONNECTION_LIST },
+ { "LIST; INVALID", 5, 1, CONNECTION_LIST },
+
+ { "LOOKUP hosts WHERE "
+ "host.name = 'host'", -1, 1, CONNECTION_LOOKUP },
+ { "LOOKUP hosts WHERE NOT "
+ "host.name = 'host'", -1, 1, CONNECTION_LOOKUP },
+ { "LOOKUP hosts WHERE "
+ "host.name =~ 'p' AND "
+ "service.name =~ 'p'", -1, 1, CONNECTION_LOOKUP },
+ { "LOOKUP hosts WHERE NOT "
+ "host.name =~ 'p' AND "
+ "service.name =~ 'p'", -1, 1, CONNECTION_LOOKUP },
+ { "LOOKUP hosts WHERE "
+ "host.name =~ 'p' AND "
+ "service.name =~ 'p' OR "
+ "service.name =~ 'r'", -1, 1, CONNECTION_LOOKUP },
+ { "LOOKUP hosts WHERE NOT "
+ "host.name =~ 'p' AND "
+ "service.name =~ 'p' OR "
+ "service.name =~ 'r'", -1, 1, CONNECTION_LOOKUP },
+
+ /* comments */
+ { "/* some comment */", -1, 0, 0 },
+ { "-- another comment", -1, 0, 0 },
+
+ /* syntax errors */
+ { "INVALID", -1, -1, 0 },
+ { "FETCH host", -1, -1, 0 },
+ { "LIST; INVALID", 8, -1, 0 },
+ { "/* some incomplete", -1, -1, 0 },
+
+ { "LOOKUP hosts", -1, -1, 0 },
+ { "LOOKUP foo WHERE "
+ "host.name = 'host'", -1, -1, 0 },
+ };
+
+ size_t i;
+ sdb_llist_t *check;
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ sdb_object_t *obj;
+ _Bool ok;
+
+ check = sdb_fe_parse(golden_data[i].query, golden_data[i].len);
+ if (golden_data[i].expected < 0)
+ ok = check == 0;
+ else
+ ok = sdb_llist_len(check) == (size_t)golden_data[i].expected;
+
+ fail_unless(ok, "sdb_fe_parse(%s) = %p (len: %zu); expected: %d",
+ golden_data[i].query, check, sdb_llist_len(check),
+ golden_data[i].expected);
+
+ if (! check)
+ continue;
+
+ if ((! golden_data[i].expected_cmd)
+ || (golden_data[i].expected <= 0)) {
+ sdb_llist_destroy(check);
+ continue;
+ }
+
+ obj = sdb_llist_get(check, 0);
+ fail_unless(SDB_CONN_NODE(obj)->cmd == golden_data[i].expected_cmd,
+ "sdb_fe_parse(%s)->cmd = %i; expected: %d",
+ golden_data[i].query, SDB_CONN_NODE(obj)->cmd,
+ golden_data[i].expected_cmd);
+ sdb_object_deref(obj);
+ sdb_llist_destroy(check);
+ }
+}
+END_TEST
+
+START_TEST(test_parse_matcher)
+{
+ struct {
+ const char *expr;
+ int len;
+ int expected;
+ } golden_data[] = {
+ /* empty expressions */
+ { NULL, -1, -1 },
+ { "", -1, -1 },
+
+ /* valid expressions */
+ { "host.name = 'localhost'", -1, MATCHER_HOST },
+ { "host.name != 'localhost'", -1, MATCHER_NOT },
+ { "host.name =~ 'host'", -1, MATCHER_HOST },
+ { "host.name !~ 'host'", -1, MATCHER_NOT },
+ { "host.name = 'localhost' -- foo", -1, MATCHER_HOST },
+ { "host.name = 'host' <garbage>", 18, MATCHER_HOST },
+ /* match hosts by service */
+ { "service.name = 'name'", -1, MATCHER_HOST },
+ { "service.name != 'name'", -1, MATCHER_NOT },
+ { "service.name =~ 'pattern'", -1, MATCHER_HOST },
+ { "service.name !~ 'pattern'", -1, MATCHER_NOT },
+ /* match hosts by attribute */
+ { "attribute.name = 'name'", -1, MATCHER_HOST },
+ { "attribute.name != 'name'", -1, MATCHER_NOT },
+ { "attribute.name =~ 'pattern'", -1, MATCHER_HOST },
+ { "attribute.name !~ 'pattern'", -1, MATCHER_NOT },
+ /* composite expressions */
+ { "host.name =~ 'pattern' AND "
+ "service.name =~ 'pattern'", -1, MATCHER_AND },
+ { "host.name =~ 'pattern' OR "
+ "service.name =~ 'pattern'", -1, MATCHER_OR },
+ { "NOT host.name = 'host'", -1, MATCHER_NOT },
+
+ /* check operator precedence */
+ { "host.name = 'name' OR "
+ "service.name = 'name' AND "
+ "attribute.name = 'name' OR "
+ "attribute.foo = 'bar'", -1, MATCHER_OR },
+ { "host.name = 'name' AND "
+ "service.name = 'name' AND "
+ "attribute.name = 'name' OR "
+ "attribute.foo = 'bar'", -1, MATCHER_OR },
+ { "host.name = 'name' AND "
+ "service.name = 'name' OR "
+ "attribute.name = 'name' AND "
+ "attribute.foo = 'bar'", -1, MATCHER_OR },
+ { "(host.name = 'name' OR "
+ "service.name = 'name') AND "
+ "(attribute.name = 'name' OR "
+ "attribute.foo = 'bar')", -1, MATCHER_AND },
+ { "NOT host.name = 'name' OR "
+ "service.name = 'name'", -1, MATCHER_OR },
+ { "NOT host.name = 'name' OR "
+ "NOT service.name = 'name'", -1, MATCHER_OR },
+ { "NOT (host.name = 'name' OR "
+ "NOT service.name = 'name')", -1, MATCHER_NOT },
+
+ /* syntax errors */
+ { "LIST", -1, -1 },
+ { "foo &^ bar", -1, -1 },
+ };
+
+ size_t i;
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ sdb_store_matcher_t *m;
+ m = sdb_fe_parse_matcher(golden_data[i].expr, golden_data[i].len);
+
+ if (golden_data[i].expected < 0) {
+ fail_unless(m == NULL,
+ "sdb_fe_parse_matcher(%s) = %p; expected: NULL",
+ golden_data[i].expr, m);
+ continue;
+ }
+
+ fail_unless(m != NULL, "sdb_fe_parse_matcher(%s) = NULL; "
+ "expected: <matcher>", golden_data[i].expr);
+ fail_unless(M(m)->type == golden_data[i].expected,
+ "sdb_fe_parse_matcher(%s) returned matcher of type %d; "
+ "expected: %d", golden_data[i].expr, M(m)->type,
+ golden_data[i].expected);
+
+ sdb_object_deref(SDB_OBJ(m));
+ }
+}
+END_TEST
+
+Suite *
+fe_parser_suite(void)
+{
+ Suite *s = suite_create("frontend::parser");
+ TCase *tc;
+
+ tc = tcase_create("core");
+ tcase_add_test(tc, test_parse);
+ tcase_add_test(tc, test_parse_matcher);
+ suite_add_tcase(s, tc);
+
+ return s;
+} /* util_parser_suite */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/t/unit/frontend/sock_test.c b/t/unit/frontend/sock_test.c
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * SysDB - t/unit/frontend/sock_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 "frontend/sock.h"
+#include "libsysdb_test.h"
+
+#include <check.h>
+
+#include <errno.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+
+#include <pthread.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+/*
+ * private variables
+ */
+
+static sdb_fe_socket_t *sock;
+
+static void
+setup(void)
+{
+ sock = sdb_fe_sock_create();
+ fail_unless(sock != NULL,
+ "sdb_fe_sock_create() = NULL; expected frontend sock object");
+} /* setup */
+
+static void
+teardown(void)
+{
+ sdb_fe_sock_destroy(sock);
+ sock = NULL;
+} /* teardown */
+
+static void
+sock_listen(char *tmp_file)
+{
+ char sock_addr[strlen("unix:") + L_tmpnam + 1];
+ char *filename;
+
+ int check;
+
+ filename = tmpnam(tmp_file);
+ fail_unless(filename != NULL,
+ "INTERNAL ERROR: tmpnam() = NULL; expected: a string");
+
+ sprintf(sock_addr, "unix:%s", tmp_file);
+ check = sdb_fe_sock_add_listener(sock, sock_addr);
+ fail_unless(check == 0,
+ "sdb_fe_sock_add_listener(%s) = %i; expected: 0",
+ sock_addr, check);
+} /* conn */
+
+/*
+ * parallel testing
+ */
+
+static void *
+sock_handler(void *data)
+{
+ sdb_fe_loop_t *loop = data;
+ int check;
+
+ check = sdb_fe_sock_listen_and_serve(sock, loop);
+ fail_unless(check == 0,
+ "sdb_fe_sock_listen_and_serve() = %i; "
+ "expected: 0 (after adding listener)", check);
+ return NULL;
+} /* sock_handler */
+
+/*
+ * tests
+ */
+
+START_TEST(test_listen_and_serve)
+{
+ sdb_fe_loop_t loop = SDB_FE_LOOP_INIT;
+
+ char tmp_file[L_tmpnam];
+ int check;
+
+ pthread_t thr;
+
+ int sock_fd;
+ struct sockaddr_un sa;
+
+ check = sdb_fe_sock_listen_and_serve(sock, &loop);
+ fail_unless(check < 0,
+ "sdb_fe_sock_listen_and_serve() = %i; "
+ "expected: <0 (before adding listeners)", check);
+
+ sock_listen(tmp_file);
+
+ loop.do_loop = 1;
+ check = pthread_create(&thr, /* attr = */ NULL, sock_handler, &loop);
+ fail_unless(check == 0,
+ "INTERNAL ERROR: pthread_create() = %i; expected: 0", check);
+
+ sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ fail_unless(sock_fd >= 0,
+ "INTERNAL ERROR: socket() = %d; expected: >= 0", sock_fd);
+
+ sa.sun_family = AF_UNIX;
+ strncpy(sa.sun_path, tmp_file, sizeof(sa.sun_path));
+
+ /* wait for socket to become available */
+ errno = ECONNREFUSED;
+ while (errno == ECONNREFUSED) {
+ check = connect(sock_fd, (struct sockaddr *)&sa, sizeof(sa));
+ if (! check)
+ break;
+
+ fail_unless(errno == ECONNREFUSED,
+ "INTERNAL ERROR: connect() = %d [errno=%d]; expected: 0",
+ check, errno);
+ }
+
+ close(sock_fd);
+
+ loop.do_loop = 0;
+ pthread_join(thr, NULL);
+
+ fail_unless(access(tmp_file, F_OK),
+ "sdb_fe_sock_listen_and_serve() did not clean up "
+ "socket %s", tmp_file);
+
+ /* should do nothing and not report errors */
+ check = sdb_fe_sock_listen_and_serve(sock, &loop);
+ fail_unless(check == 0,
+ "sdb_fe_sock_listen_and_serve() = %i; "
+ "expected: <0 (do_loop == 0)", check);
+ fail_unless(access(tmp_file, F_OK),
+ "sdb_fe_sock_listen_and_serve() recreated socket "
+ "(do_loop == 0)");
+}
+END_TEST
+
+Suite *
+fe_sock_suite(void)
+{
+ Suite *s = suite_create("frontend::sock");
+ TCase *tc;
+
+ tc = tcase_create("core");
+ tcase_add_checked_fixture(tc, setup, teardown);
+ tcase_add_test(tc, test_listen_and_serve);
+ suite_add_tcase(s, tc);
+
+ return s;
+} /* util_unixsock_suite */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/t/unit/libsysdb_net_test.c b/t/unit/libsysdb_net_test.c
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * SysDB - t/unit/libsysdb_net_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.
+ */
+
+/*
+ * testing component involving networking operations
+ */
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include "libsysdb_test.h"
+
+#include <check.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main(void)
+{
+ int failed = 0;
+ size_t i;
+
+ suite_creator_t creators[] = {
+#ifdef HAVE_FOPENCOOKIE
+ { util_unixsock_suite, NULL },
+#else
+ { NULL, "Skipping util::unixsock; missing fopencookie" },
+#endif /* HAVE_FOPENCOOKIE */
+ };
+
+ putenv("TZ=UTC");
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(creators); ++i) {
+ SRunner *sr;
+ Suite *s;
+
+ if (creators[i].msg)
+ printf("%s\n", creators[i].msg);
+
+ if (!creators[i].creator)
+ continue;
+
+ s = creators[i].creator();
+ sr = srunner_create(s);
+ srunner_run_all(sr, CK_NORMAL);
+ failed += srunner_ntests_failed(sr);
+ srunner_free(sr);
+ }
+
+ return failed;
+} /* main */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/t/unit/libsysdb_test.c b/t/unit/libsysdb_test.c
--- /dev/null
+++ b/t/unit/libsysdb_test.c
@@ -0,0 +1,82 @@
+/*
+ * SysDB - t/unit/libsysdb_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.
+ */
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include "libsysdb_test.h"
+
+#include <check.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main(void)
+{
+ int failed = 0;
+ size_t i;
+
+ suite_creator_t creators[] = {
+ { core_data_suite, NULL },
+ { core_object_suite, NULL },
+ { core_store_suite, NULL },
+ { core_store_lookup_suite, NULL },
+ { core_time_suite, NULL },
+ { fe_conn_suite, NULL },
+ { fe_parser_suite, NULL },
+ { fe_sock_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;
+
+ if (creators[i].msg)
+ printf("%s\n", creators[i].msg);
+
+ if (!creators[i].creator)
+ continue;
+
+ s = creators[i].creator();
+ sr = srunner_create(s);
+ srunner_run_all(sr, CK_NORMAL);
+ failed += srunner_ntests_failed(sr);
+ srunner_free(sr);
+ }
+
+ return failed;
+} /* main */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/t/unit/libsysdb_test.h b/t/unit/libsysdb_test.h
--- /dev/null
+++ b/t/unit/libsysdb_test.h
@@ -0,0 +1,117 @@
+/*
+ * SysDB - t/unit/libsysdb_test.h
+ * 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.
+ */
+
+#ifndef T_LIBSYSDB_H
+#define T_LIBSYSDB_H 1
+
+#include "sysdb.h"
+#include "core/object.h"
+
+#include "libsysdb_testutils.h"
+
+#include <check.h>
+#include <string.h>
+
+/*
+ * private testing helpers
+ */
+
+/* static string object:
+ * Any such object may is of type sdb_object_t but may never be destroyed. */
+#define SSTRING_OBJ(name) { \
+ /* type = */ { sizeof(sdb_object_t), NULL, NULL }, \
+ /* ref_cnt = */ 1, /* name = */ (name) }
+
+/*
+ * test-related data-types
+ */
+
+typedef struct {
+ Suite *(*creator)(void);
+ const char *msg;
+} suite_creator_t;
+
+/*
+ * test suites
+ */
+
+/* t/core/data_test */
+Suite *
+core_data_suite(void);
+
+/* t/core/object_test */
+Suite *
+core_object_suite(void);
+
+/* t/core/store_test */
+Suite *
+core_store_suite(void);
+
+/* t/core/store_lookup_test */
+Suite *
+core_store_lookup_suite(void);
+
+/* t/core/time_test */
+Suite *
+core_time_suite(void);
+
+/* t/frontend/connection_test */
+Suite *
+fe_conn_suite(void);
+
+/* t/frontend/parser_test */
+Suite *
+fe_parser_suite(void);
+
+/* t/frontend/sock_test */
+Suite *
+fe_sock_suite(void);
+
+/* t/utils/channel_test */
+Suite *
+util_channel_suite(void);
+
+/* t/utils/dbi_test */
+Suite *
+util_dbi_suite(void);
+
+/* t/utils/llist_test */
+Suite *
+util_llist_suite(void);
+
+/* t/utils/strbuf_test */
+Suite *
+util_strbuf_suite(void);
+
+/* t/utils/unixsock_test */
+Suite *
+util_unixsock_suite(void);
+
+#endif /* T_LIBSYSDB_H */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/t/unit/libsysdb_testutils.c b/t/unit/libsysdb_testutils.c
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * SysDB - t/unit/libsysdb_testutils.c
+ * Copyright (C) 2014 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.
+ */
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include "libsysdb_testutils.h"
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <regex.h>
+
+int
+sdb_regmatches(const char *regex, const char *string)
+{
+ regex_t reg;
+ int status;
+
+ status = regcomp(®, regex, REG_EXTENDED | REG_NOSUB);
+ if (status)
+ return status;
+
+ status = regexec(®, string, /* matches = */ 0, NULL, /* flags = */ 0);
+ regfree(®);
+ return status;
+} /* sdb_regmatches */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/t/unit/libsysdb_testutils.h b/t/unit/libsysdb_testutils.h
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * SysDB - t/unit/libsysdb_testutils.h
+ * Copyright (C) 2014 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.
+ */
+
+/*
+ * Utility functions for test suites.
+ */
+
+#ifndef T_LIBSYSDB_UTILS_H
+#define T_LIBSYSDB_UTILS_H 1
+
+/*
+ * sdb_regmatches:
+ * Check if a regex matches a string.
+ *
+ * Returns:
+ * - 0 if the regex matches
+ * - a non-zero error value else (see regcomp(3) for details)
+ */
+int
+sdb_regmatches(const char *regex, const char *string);
+
+#endif /* T_LIBSYSDB_UTILS_H */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/t/unit/utils/channel_test.c b/t/unit/utils/channel_test.c
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ * SysDB - t/unit/utils/channel_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 "utils/channel.h"
+#include "libsysdb_test.h"
+
+#include <check.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <stdint.h>
+
+static struct {
+ int data;
+ int expected_write;
+ int expected_read;
+} golden_data_int[] = {
+ { 5, 0, 0 },
+ { 15, 0, 0 },
+ { -3, 0, 0 },
+ { INT_MAX, 0, 0 },
+ { 27, 0, 0 },
+ { 42, 0, 0 },
+ { 6, 0, 0 },
+ { 2854, 0, 0 },
+ { 10562, 0, 0 },
+ { 0, 0, 0 },
+
+ /* exceeding buffer size */
+ { 20, -1, -1 },
+ { 42, -1, -1 },
+};
+
+static struct {
+ char *data;
+ int expected_write;
+ int expected_read;
+} golden_data_string[] = {
+ { "c", 0, 0 },
+ { "", 0, 0 },
+ { "abc", 0, 0 },
+ { "foobar", 0, 0 },
+ { "qux", 0, 0 },
+ { "a b c", 0, 0 },
+ { "123", 0, 0 },
+ { "xyz", 0, 0 },
+ { "b", 0, 0 },
+ { "a", 0, 0 },
+
+ /* exceeding buffer size */
+ { "err1", -1, -1 },
+ { "err2", -1, -1 },
+};
+
+static sdb_channel_t *chan;
+
+static void
+setup_int(void)
+{
+ chan = sdb_channel_create(10, sizeof(int));
+ fail_unless(chan != NULL,
+ "sdb_channel_create(10, sizeof(int)) = NULL; "
+ "expected list object");
+} /* setup_int */
+
+static void
+setup_string(void)
+{
+ chan = sdb_channel_create(10, sizeof(char *));
+ fail_unless(chan != NULL,
+ "sdb_chan_create(10, sizeof(char *))) = NULL; "
+ "expected channel object");
+} /* setup_string */
+
+static void
+teardown(void)
+{
+ sdb_channel_destroy(chan);
+ chan = NULL;
+} /* teardown */
+
+START_TEST(test_create)
+{
+ chan = sdb_channel_create(0, 0);
+ fail_unless(chan == NULL,
+ "sdb_channel_create(0, 0) = %p; expected: NULL", chan);
+
+ chan = sdb_channel_create(0, 1);
+ fail_unless(chan != NULL,
+ "sdb_channel_create(0, 1) = NULL; expected: channel object");
+ sdb_channel_destroy(chan);
+
+ chan = sdb_channel_create(42, 23);
+ fail_unless(chan != NULL,
+ "sdb_channel_create(32, 23) = NULL; expected: channel object");
+ sdb_channel_destroy(chan);
+}
+END_TEST
+
+START_TEST(test_write_read)
+{
+ uint32_t data;
+ int check;
+
+ chan = sdb_channel_create(0, 1);
+ fail_unless(chan != NULL,
+ "sdb_channel_create(0, 0) = NULL; expected: channel object");
+
+ data = 0x00ffff00;
+ check = sdb_channel_write(chan, &data);
+ fail_unless(!check, "sdb_channel_write() = %i; expected: 0", check);
+ check = sdb_channel_write(chan, &data);
+ fail_unless(check, "sdb_channel_write() = 0; expected: <0");
+
+ data = 0xffffffff;
+ check = sdb_channel_read(chan, &data);
+ fail_unless(check == 0,
+ "sdb_channel_read() = %d; expected: 0", check);
+ /* result depends on endianess */
+ fail_unless((data == 0xffffff00) || (data == 0x00ffffff),
+ "sdb_channel_read() returned data %x; "
+ "expected: 0xffffff00 || 0x00ffffff", data);
+
+ sdb_channel_destroy(chan);
+}
+END_TEST
+
+START_TEST(test_select)
+{
+ struct timespec ts = { 0, 10 };
+ int check;
+ int data;
+
+ chan = sdb_channel_create(0, 1);
+
+ errno = 0;
+ check = sdb_channel_select(chan, &data, NULL, NULL, NULL, &ts);
+ fail_unless(check < ETIMEDOUT,
+ "sdb_channel_select() = %i; expected: <0", check);
+ fail_unless(errno == ETIMEDOUT,
+ "sdb_channel_select() set errno to %i; expected: %i (ETIMEDOUT)",
+ errno, ETIMEDOUT);
+
+ check = sdb_channel_select(chan, NULL, NULL, &data, NULL, NULL);
+ fail_unless(! check, "sdb_channel_select() = %i; expected: 0", check);
+ check = sdb_channel_select(chan, NULL, NULL, &data, NULL, &ts);
+ fail_unless(! check, "sdb_channel_select() = %i; expected: 0", check);
+
+ sdb_channel_destroy(chan);
+}
+END_TEST
+
+START_TEST(test_write_int)
+{
+ size_t i;
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data_int); ++i) {
+ int data = golden_data_int[i].data;
+ int expected = golden_data_int[i].expected_write;
+
+ int check = sdb_channel_write(chan, &data);
+ fail_unless(check == expected,
+ "sdb_channel_write(chan, %i) = %i; expected: %i",
+ data, check, expected);
+ }
+}
+END_TEST
+
+START_TEST(test_read_int)
+{
+ size_t i;
+
+ /* populate */
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data_int); ++i) {
+ int data = golden_data_int[i].data;
+ int expected = golden_data_int[i].expected_write;
+
+ int check = sdb_channel_write(chan, &data);
+ fail_unless(check == expected,
+ "sdb_channel_write(chan, %i) = %i; expected: %i",
+ data, check, expected);
+ }
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data_int); ++i) {
+ int data = (int)i;
+ int expected = golden_data_int[i].expected_read;
+
+ int check = sdb_channel_read(chan, &data);
+ fail_unless(check == expected,
+ "sdb_channel_read(chan, %i) = %i; expected: %i",
+ data, check, expected);
+ if (check) {
+ fail_unless(data == (int)i,
+ "sdb_channel_read() modified data to '%i'; "
+ "expected: no modification", data);
+ }
+ else {
+ fail_unless(data == golden_data_int[i].data,
+ "sdb_channel_read() returned data %i; expected: %i",
+ data, golden_data_int[i].data);
+ }
+ }
+}
+END_TEST
+
+START_TEST(test_write_read_int)
+{
+ size_t i;
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data_int); ++i) {
+ int data = golden_data_int[i].data;
+ int check = sdb_channel_write(chan, &data);
+ fail_unless(check == 0,
+ "sdb_channel_write(chan, %i) = %i; expected: 0",
+ data, check);
+
+ data = (int)i;
+ check = sdb_channel_read(chan, &data);
+ fail_unless(check == 0,
+ "sdb_channel_read(chan, %i) = %i; expected: 0",
+ data, check);
+ if (check) {
+ fail_unless(data == (int)i,
+ "sdb_channel_read() modified data to '%i'; "
+ "expected: no modification", data);
+ }
+ else {
+ fail_unless(data == golden_data_int[i].data,
+ "sdb_channel_read() returned data %i; expected: %i",
+ data, golden_data_int[i].data);
+ }
+ }
+}
+END_TEST
+
+START_TEST(test_write_string)
+{
+ size_t i;
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data_string); ++i) {
+ char *data = golden_data_string[i].data;
+ int expected = golden_data_string[i].expected_write;
+
+ int check = sdb_channel_write(chan, &data);
+ fail_unless(check == expected,
+ "sdb_channel_write(chan, '%s') = %i; expected: %i",
+ data, check, expected);
+ }
+}
+END_TEST
+
+START_TEST(test_read_string)
+{
+ size_t i;
+
+ /* populate */
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data_string); ++i) {
+ char *data = golden_data_string[i].data;
+ int expected = golden_data_string[i].expected_write;
+
+ int check = sdb_channel_write(chan, &data);
+ fail_unless(check == expected,
+ "sdb_channel_write(chan, '%s') = %i; expected: %i",
+ data, check, expected);
+ }
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data_string); ++i) {
+ char *data = NULL;
+ int expected = golden_data_string[i].expected_read;
+
+ int check = sdb_channel_read(chan, &data);
+ fail_unless(check == expected,
+ "sdb_channel_read(chan, '') = %i; expected: %i",
+ check, expected);
+ if (check) {
+ fail_unless(data == NULL,
+ "sdb_channel_read() modified data to '%s'; "
+ "expected: no modification", data);
+ }
+ else {
+ fail_unless(data != NULL,
+ "sdb_channel_read() did not return any data");
+ fail_unless(!strcmp(data, golden_data_string[i].data),
+ "sdb_channel_read() returned data '%s'; expected: '%s'",
+ data, golden_data_string[i].data);
+ }
+ }
+}
+END_TEST
+
+START_TEST(test_write_read_string)
+{
+ size_t i;
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data_string); ++i) {
+ char *data = golden_data_string[i].data;
+ int check = sdb_channel_write(chan, &data);
+ fail_unless(check == 0,
+ "sdb_channel_write(chan, '%s') = %i; expected: 0",
+ data, check);
+
+ data = NULL;
+ check = sdb_channel_read(chan, &data);
+ fail_unless(check == 0,
+ "sdb_channel_read(chan, '') = %i; expected: 0", check);
+ if (check) {
+ fail_unless(data == NULL,
+ "sdb_channel_read() modified data to '%s'; "
+ "expected: no modifications", data);
+ }
+ else {
+ fail_unless(data != NULL,
+ "sdb_channel_read() did not return any data");
+ fail_unless(!strcmp(data, golden_data_string[i].data),
+ "sdb_channel_read() returned data '%s'; expected: '%s'",
+ data, golden_data_string[i].data);
+ }
+ }
+}
+END_TEST
+
+Suite *
+util_channel_suite(void)
+{
+ Suite *s = suite_create("utils::channel");
+ TCase *tc;
+
+ tc = tcase_create("core");
+ tcase_add_test(tc, test_create);
+ tcase_add_test(tc, test_write_read);
+ tcase_add_test(tc, test_select);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("integer");
+ tcase_add_checked_fixture(tc, setup_int, teardown);
+ tcase_add_test(tc, test_write_int);
+ tcase_add_test(tc, test_read_int);
+ tcase_add_test(tc, test_write_read_int);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("string");
+ tcase_add_checked_fixture(tc, setup_string, teardown);
+ tcase_add_test(tc, test_write_string);
+ tcase_add_test(tc, test_read_string);
+ tcase_add_test(tc, test_write_read_string);
+ suite_add_tcase(s, tc);
+
+ return s;
+} /* util_llist_suite */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/t/unit/utils/dbi_test.c b/t/unit/utils/dbi_test.c
--- /dev/null
+++ b/t/unit/utils/dbi_test.c
@@ -0,0 +1,645 @@
+/*
+ * SysDB - t/unit/utils/dbi_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.
+ */
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include "libsysdb_test.h"
+#include "utils/dbi.h"
+
+#include <check.h>
+#include <dbi/dbi.h>
+
+#define TEST_MAGIC ((void *)0x1337)
+
+/*
+ * private data-types
+ */
+typedef union {
+ long long integer;
+ double decimal;
+ const char *string;
+ time_t datetime;
+ struct {
+ size_t length;
+ const unsigned char *datum;
+ } binary;
+} mock_data_t;
+
+typedef struct {
+ const char *name;
+ unsigned long long nrows;
+ unsigned long long current_row;
+ unsigned int nfields;
+ unsigned short *field_types;
+ char **field_names;
+} mock_query_t;
+
+/*
+ * private variables
+ */
+
+static sdb_dbi_client_t *client;
+
+/*
+ * mock queries
+ */
+
+/* field definitions */
+static unsigned short field_types[] = {
+ DBI_TYPE_INTEGER,
+ DBI_TYPE_DECIMAL,
+ DBI_TYPE_STRING,
+ DBI_TYPE_DATETIME,
+ DBI_TYPE_BINARY,
+};
+static char *field_names[] = {
+ "field0",
+ "field1",
+ "field2",
+ "field3",
+ "field4",
+};
+
+#define DATUM(p) ((const unsigned char *)(p))
+static mock_data_t golden_data[][5] = {
+ {
+ { .integer = 1234 },
+ { .decimal = 1.234 },
+ { .string = "abcd" },
+ { .datetime = 0 },
+ { .binary = { 1, DATUM("a") } },
+ },
+ {
+ { .integer = 2345 },
+ { .decimal = 23.45 },
+ { .string = "bcde" },
+ { .datetime = 1 },
+ { .binary = { 4, DATUM("bcde") } },
+ },
+ {
+ { .integer = 3456 },
+ { .decimal = 345.6 },
+ { .string = "cd" },
+ { .datetime = 2 },
+ { .binary = { 0, DATUM(NULL) } },
+ },
+ {
+ { .integer = 4567 },
+ { .decimal = 4567 },
+ { .string = "d" },
+ { .datetime = 3 },
+ { .binary = { 13, DATUM("defghijklmnop") } },
+ },
+ {
+ { .integer = 5678 },
+ { .decimal = 56.78 },
+ { .string = "efgh" },
+ { .datetime = 4 },
+ { .binary = { 5, DATUM("efghi") } },
+ },
+};
+
+/* query definitions */
+static mock_query_t mock_queries[] = {
+ { "mockquery0", 5, 1, 0, NULL, NULL },
+ { "mockquery1", 0, 0, 1, field_types, field_names },
+ { "mockquery2", 1, 1, 1, field_types, field_names },
+ { "mockquery3", 2, 1, 1, field_types, field_names },
+ { "mockquery4", 5, 1, 1, field_types, field_names },
+ { "mockquery5", 5, 1, 2, field_types, field_names },
+ { "mockquery6", 5, 1, 3, field_types, field_names },
+ { "mockquery7", 5, 1, 4, field_types, field_names },
+ { "mockquery8", 5, 1, 5, field_types, field_names },
+};
+
+static mock_query_t *current_query = NULL;
+
+/*
+ * mocked functions
+ */
+
+/* dbi_driver, dbi_conn, dbi_result are void pointers */
+
+dbi_driver
+dbi_driver_open(const char *name)
+{
+ if (strcmp(name, "mockdriver"))
+ return NULL;
+ return (dbi_driver)strdup(name);
+} /* dbi_driver_open */
+
+dbi_driver
+dbi_driver_list(dbi_driver curr)
+{
+ if (!curr)
+ return "mockdriver";
+ return NULL;
+} /* dbi_driver_list */
+
+const char *
+dbi_driver_get_name(dbi_driver driver)
+{
+ return (const char *)driver;
+} /* dbi_driver_get_name */
+
+int
+dbi_conn_set_option(dbi_conn __attribute__((unused)) conn,
+ const char __attribute__((unused)) *key,
+ const char __attribute__((unused)) *value)
+{
+ return 0;
+} /* dbi_conn_set_option */
+
+const char *
+dbi_conn_get_option_list(dbi_conn __attribute__((unused)) conn,
+ const char __attribute__((unused)) *key)
+{
+ return NULL;
+} /* dbi_conn_get_option_list */
+
+dbi_conn
+dbi_conn_open(dbi_driver driver)
+{
+ if (strcmp((const char *)driver, "mockdriver"))
+ return NULL;
+ return (dbi_conn)"mockconnection";
+} /* dbi_conn_open */
+
+static unsigned long long dbi_conn_connect_called = 0;
+int
+dbi_conn_connect(dbi_conn conn)
+{
+ ++dbi_conn_connect_called;
+ if (strcmp((const char *)conn, "mockconnection"))
+ return DBI_ERROR_NOCONN;
+ return 0;
+} /* dbi_conn_connect */
+
+int
+dbi_conn_ping(dbi_conn conn)
+{
+ if (strcmp((const char *)conn, "mockconnection"))
+ return 0;
+ return 1;
+} /* dbi_conn_connect */
+
+void
+dbi_conn_close(dbi_conn __attribute__((unused)) conn)
+{
+ return;
+} /* dbi_conn_close */
+
+int
+dbi_conn_error(dbi_conn conn, const char **errmsg)
+{
+ if ((! conn) || (strcmp((const char *)conn, "mockconnection")))
+ return DBI_ERROR_BADOBJECT;
+ if (errmsg)
+ *errmsg = "mockerror";
+ return DBI_ERROR_UNSUPPORTED;
+} /* dbi_conn_error */
+
+static unsigned long long dbi_conn_query_called = 0;
+dbi_result
+dbi_conn_query(dbi_conn conn, const char __attribute__((unused)) *statement)
+{
+ size_t i;
+
+ ++dbi_conn_query_called;
+ if (strcmp((const char *)conn, "mockconnection"))
+ return NULL;
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(mock_queries); ++i) {
+ if (!strcmp(mock_queries[i].name, statement)) {
+ current_query = &mock_queries[i];
+ return (dbi_result)current_query;
+ }
+ }
+ return NULL;
+} /* dbi_conn_query */
+
+unsigned long long
+dbi_result_get_numrows(dbi_result res)
+{
+ mock_query_t *q = res;
+ if (! q)
+ return DBI_ROW_ERROR;
+ return q->nrows;
+} /* dbi_result_get_numrows */
+
+unsigned int
+dbi_result_get_numfields(dbi_result res)
+{
+ mock_query_t *q = res;
+ if (! q)
+ return DBI_FIELD_ERROR;
+ return q->nfields;
+} /* dbi_result_get_numfields */
+
+unsigned short
+dbi_result_get_field_type_idx(dbi_result res, unsigned int i)
+{
+ mock_query_t *q = res;
+ if ((! q) || (i > q->nfields))
+ return DBI_TYPE_ERROR;
+ return q->field_types[i - 1];
+} /* dbi_result_get_field_type_idx */
+
+const char *
+dbi_result_get_field_name(dbi_result res, unsigned int i)
+{
+ mock_query_t *q = res;
+ if ((! q) || (i > q->nfields))
+ return NULL;
+ return q->field_names[i - 1];
+} /* dbi_result_get_field_name */
+
+int
+dbi_result_seek_row(dbi_result res, unsigned long long n)
+{
+ mock_query_t *q = res;
+ if ((! q) || (n > q->nrows))
+ return 0;
+
+ q->current_row = n;
+ return 1;
+} /* dbi_result_seek_row */
+
+static mock_data_t
+get_golden_data(dbi_result res, unsigned int i) {
+ mock_query_t *q = res;
+ fail_unless(q != NULL, "dbi_result_get_*_idx() called with "
+ "NULL result data; expected valid result object");
+
+ /* this information is managed by seek_row and, thus,
+ * should never be invalid */
+ fail_unless(q->current_row && q->current_row <= q->nrows,
+ "INTERNAL ERROR: current row out of range");
+
+ fail_unless(i && i <= q->nfields,
+ "dbi_result_get_*_idx() called with index out of range; "
+ "got: %u; expected [1, %u]", i, q->nfields);
+ return golden_data[q->current_row - 1][i - 1];
+} /* get_golden_data */
+
+long long
+dbi_result_get_longlong_idx(dbi_result res, unsigned int i)
+{
+ fail_unless(current_query->field_types[i - 1] == DBI_TYPE_INTEGER,
+ "dbi_result_get_longlong_idx() called for non-integer "
+ "column type %u", current_query->field_types[i - 1]);
+ return get_golden_data(res, i).integer;
+} /* dbi_result_get_longlong_idx */
+
+double
+dbi_result_get_double_idx(dbi_result res, unsigned int i)
+{
+ fail_unless(current_query->field_types[i - 1] == DBI_TYPE_DECIMAL,
+ "dbi_result_get_double_idx() called for non-decimal "
+ "column type %u", current_query->field_types[i - 1]);
+ return get_golden_data(res, i).decimal;
+} /* dbi_result_get_double_idx */
+
+const char *
+dbi_result_get_string_idx(dbi_result res, unsigned int i)
+{
+ fail_unless(current_query->field_types[i - 1] == DBI_TYPE_STRING,
+ "dbi_result_get_string_idx() called for non-string "
+ "column type %u", current_query->field_types[i - 1]);
+ return get_golden_data(res, i).string;
+} /* dbi_result_get_string_idx */
+
+char *
+dbi_result_get_string_copy_idx(dbi_result res, unsigned int i)
+{
+ fail_unless(current_query->field_types[i - 1] == DBI_TYPE_STRING,
+ "dbi_result_get_string_copy_idx() called for non-string "
+ "column type %u", current_query->field_types[i - 1]);
+ if (! get_golden_data(res, i).string)
+ return NULL;
+ return strdup(get_golden_data(res, i).string);
+} /* dbi_result_get_string_copy_idx */
+
+time_t
+dbi_result_get_datetime_idx(dbi_result res, unsigned int i)
+{
+ fail_unless(current_query->field_types[i - 1] == DBI_TYPE_DATETIME,
+ "dbi_result_get_datetime_idx() called for non-datetime "
+ "column type %u", current_query->field_types[i - 1]);
+ return get_golden_data(res, i).datetime;
+} /* dbi_result_get_datetime_idx */
+
+size_t
+dbi_result_get_field_length_idx(dbi_result res, unsigned int i)
+{
+ /* this will check if the parameters are valid */
+ get_golden_data(res, i);
+
+ switch (current_query->field_types[i - 1]) {
+ case DBI_TYPE_INTEGER:
+ return sizeof(long long);
+ break;
+ case DBI_TYPE_DECIMAL:
+ return sizeof(double);
+ break;
+ case DBI_TYPE_STRING:
+ return strlen(get_golden_data(res, i).string) + 1;
+ break;
+ case DBI_TYPE_DATETIME:
+ return sizeof(time_t);
+ break;
+ case DBI_TYPE_BINARY:
+ return get_golden_data(res, i).binary.length;
+ break;
+ }
+
+ fail("INTERNAL ERROR: dbi_result_get_field_length_idx() "
+ "called for unexpected field type %u",
+ current_query->field_types[i - 1]);
+ return 0;
+} /* dbi_result_get_field_length_idx */
+
+const unsigned char *
+dbi_result_get_binary_idx(dbi_result res, unsigned int i)
+{
+ fail_unless(current_query->field_types[i - 1] == DBI_TYPE_BINARY,
+ "dbi_result_get_binary_idx() called for non-binary "
+ "column type %u", current_query->field_types[i - 1]);
+ return get_golden_data(res, i).binary.datum;
+} /* dbi_result_get_binary_idx */
+
+unsigned char *
+dbi_result_get_binary_copy_idx(dbi_result res, unsigned int i)
+{
+ const char *data;
+ fail_unless(current_query->field_types[i - 1] == DBI_TYPE_BINARY,
+ "dbi_result_get_binary_copy_idx() called for non-binary "
+ "column type %u", current_query->field_types[i - 1]);
+ data = (const char *)get_golden_data(res, i).binary.datum;
+ if (! data)
+ return NULL;
+ return (unsigned char *)strdup(data);
+} /* dbi_result_get_binary_copy_idx */
+
+static unsigned long long dbi_result_free_called = 0;
+int
+dbi_result_free(dbi_result res)
+{
+ mock_query_t *q = res;
+
+ ++dbi_result_free_called;
+ if (! q)
+ return -1;
+
+ current_query = NULL;
+ return 0;
+} /* dbi_result_free */
+
+/*
+ * private helper functions
+ */
+
+static void
+setup(void)
+{
+ client = sdb_dbi_client_create("mockdriver", "mockdatabase");
+ fail_unless(client != NULL,
+ "sdb_dbi_client_create() = NULL; expected client object");
+
+ dbi_conn_connect_called = 0;
+} /* setup */
+
+static void
+connect(void)
+{
+ int check = sdb_dbi_client_connect(client);
+ fail_unless(check == 0,
+ "sdb_dbi_client_connect() = %i; expected: 0", check);
+} /* connect */
+
+static void
+teardown(void)
+{
+ sdb_dbi_client_destroy(client);
+ client = NULL;
+} /* teardown */
+
+static unsigned long long query_callback_called = 0;
+static int
+query_callback(sdb_dbi_client_t *c,
+ size_t n, sdb_data_t *data, sdb_object_t *user_data)
+{
+ size_t i;
+
+ ++query_callback_called;
+
+ fail_unless(c == client,
+ "query callback received unexpected client = %p; "
+ "expected: %p", c, client);
+ fail_unless(n == current_query->nfields,
+ "query callback received n = %i; expected: %i",
+ n, current_query->nfields);
+ fail_unless(data != NULL,
+ "query callback received data = NULL; expected: valid data");
+ fail_unless(user_data == TEST_MAGIC,
+ "query callback received user_data = %p; expected: %p",
+ user_data, TEST_MAGIC);
+
+ for (i = 0; i < n; ++i) {
+ int expected_type = DBI_TYPE_TO_SC(current_query->field_types[i]);
+ mock_data_t expected_data;
+
+ fail_unless((int)data[i].type == expected_type,
+ "query callback received unexpected type %i for "
+ "column %zu; expected: %i", data[i].type, i,
+ expected_type);
+
+ expected_data = golden_data[current_query->current_row - 1][i];
+ switch (expected_type) {
+ case SDB_TYPE_INTEGER:
+ fail_unless(data[i].data.integer == expected_data.integer,
+ "query callback received unexpected data %lli "
+ "for column %zu; expected: %lli",
+ data[i].data.integer, i, expected_data.integer);
+ break;
+ case SDB_TYPE_DECIMAL:
+ fail_unless(data[i].data.decimal == expected_data.decimal,
+ "query callback received unexpected data %g "
+ "for column %zu; expected: %g",
+ data[i].data.decimal, i, expected_data.decimal);
+ break;
+ case SDB_TYPE_STRING:
+ fail_unless(!strcmp(data[i].data.string, expected_data.string),
+ "query callback received unexpected data %s "
+ "for column %zu; expected: %s",
+ data[i].data.string, i, expected_data.string);
+ break;
+ case SDB_TYPE_DATETIME:
+ fail_unless(data[i].data.datetime
+ == SECS_TO_SDB_TIME(expected_data.datetime),
+ "query callback received unexpected data "PRIscTIME
+ " for column %zu; expected: "PRIscTIME,
+ data[i].data.integer, i,
+ SECS_TO_SDB_TIME(expected_data.integer));
+ break;
+ case SDB_TYPE_BINARY:
+ fail_unless(data[i].data.binary.length ==
+ expected_data.binary.length,
+ "query callback received unexpected "
+ "binary data length %zu for column %zu; "
+ "expected: %lli", data[i].data.binary.length, i,
+ expected_data.binary.length);
+ fail_unless(!memcmp(data[i].data.binary.datum,
+ expected_data.binary.datum,
+ expected_data.binary.length),
+ "query callback received unexpected binary data %p "
+ "for column %zu; expected: %p",
+ data[i].data.binary.datum, i,
+ expected_data.binary.datum);
+ break;
+ default:
+ fail("INTERNAL ERROR: query callback received "
+ "unknown type %i for column %zu",
+ expected_type, i);
+ }
+ }
+ return 0;
+} /* query_callback */
+
+/*
+ * tests
+ */
+
+START_TEST(test_sdb_dbi_client_connect)
+{
+ int check = sdb_dbi_client_connect(client);
+ fail_unless(check == 0,
+ "sdb_dbi_client_connect() = %i; expected: 0", check);
+
+ fail_unless(dbi_conn_connect_called == 1,
+ "sdb_dbi_client_create() called dbi_conn_connect %i times; "
+ "expected: 1", dbi_conn_connect_called);
+}
+END_TEST
+
+START_TEST(test_sdb_dbi_client_check_conn)
+{
+ int check = sdb_dbi_client_check_conn(client);
+ fail_unless(check == 0,
+ "sdb_dbi_client_check_conn() = %i; expected: 0", check);
+
+ /* the first call will actually connect to the database */
+ fail_unless(dbi_conn_connect_called == 1,
+ "sdb_dbi_client_check_conn() called dbi_conn_connect %i times; "
+ "expected: 1", dbi_conn_connect_called);
+
+ dbi_conn_connect_called = 0;
+ check = sdb_dbi_client_check_conn(client);
+ fail_unless(check == 0,
+ "sdb_dbi_client_check_conn() = %i; expected: 0", check);
+
+ /* should not reconnect */
+ fail_unless(dbi_conn_connect_called == 0,
+ "sdb_dbi_client_check_conn() called dbi_conn_connect %i time(s); "
+ "expected: 0", dbi_conn_connect_called);
+}
+END_TEST
+
+START_TEST(test_sdb_dbi_exec_query)
+{
+ size_t i;
+
+ int check = sdb_dbi_exec_query(client, "mockquery0", query_callback,
+ /* user_data = */ TEST_MAGIC, /* n = */ 0);
+ /* not connected yet */
+ fail_unless(check < 0,
+ "sdb_dbi_exec_query() = %i; expected: < 0", check);
+
+ connect();
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(mock_queries); ++i) {
+ mock_query_t *q = &mock_queries[i];
+
+ unsigned long long expected_callback_calls = 0;
+
+ dbi_conn_query_called = 0;
+ query_callback_called = 0;
+ dbi_result_free_called = 0;
+
+ /* sdb_dbi_exec_query will only use as many type arguments are needed,
+ * so we can safely pass in the maximum number of arguments required
+ * on each call */
+ check = sdb_dbi_exec_query(client, q->name, query_callback,
+ /* user_data = */ TEST_MAGIC, /* n = */ (int)q->nfields,
+ SDB_TYPE_INTEGER, SDB_TYPE_DECIMAL, SDB_TYPE_STRING,
+ SDB_TYPE_DATETIME, SDB_TYPE_BINARY);
+ fail_unless(check == 0,
+ "sdb_dbi_exec_query() = %i; expected: 0", check);
+
+ fail_unless(dbi_conn_query_called == 1,
+ "sdb_dbi_exec_query() called dbi_conn_query %i times; "
+ "expected: 1", dbi_conn_query_called);
+
+ if (q->nfields)
+ expected_callback_calls = q->nrows;
+
+ fail_unless(query_callback_called == expected_callback_calls,
+ "sdb_dbi_exec_query() did not call the registered callback "
+ "for each result row; got %i call%s; expected: 0",
+ query_callback_called,
+ (query_callback_called == 1) ? "" : "s");
+
+ fail_unless(dbi_result_free_called == 1,
+ "sdb_dbi_exec_query() did not free the query result object");
+ }
+}
+END_TEST
+
+/*
+ * test API
+ */
+
+Suite *
+util_dbi_suite(void)
+{
+ Suite *s = suite_create("utils::dbi");
+ TCase *tc;
+
+ tc = tcase_create("core");
+ tcase_add_checked_fixture(tc, setup, teardown);
+ tcase_add_test(tc, test_sdb_dbi_client_connect);
+ tcase_add_test(tc, test_sdb_dbi_client_check_conn);
+ tcase_add_test(tc, test_sdb_dbi_exec_query);
+ suite_add_tcase(s, tc);
+
+ return s;
+} /* util_llist_suite */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/t/unit/utils/llist_test.c b/t/unit/utils/llist_test.c
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * SysDB - t/unit/utils/llist_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 "utils/llist.h"
+#include "libsysdb_test.h"
+
+#include <check.h>
+
+/*
+ * private data types
+ */
+
+static sdb_object_t golden_data[] = {
+ SSTRING_OBJ("abc"),
+ SSTRING_OBJ("bcd"),
+ SSTRING_OBJ("cde"),
+ SSTRING_OBJ("def"),
+ SSTRING_OBJ("efg"),
+ SSTRING_OBJ("fgh"),
+ SSTRING_OBJ("ghi")
+};
+
+static char *unused_names[] = {
+ "xyz",
+ "yza",
+ "zab"
+};
+
+static sdb_llist_t *list;
+
+static void
+setup(void)
+{
+ list = sdb_llist_create();
+ fail_unless(list != NULL,
+ "sdb_llist_create() = NULL; expected list object");
+} /* setup */
+
+static void
+teardown(void)
+{
+ sdb_llist_destroy(list);
+ list = NULL;
+} /* teardown */
+
+/* populate the list with the golden data in the specified order */
+static void
+populate(void)
+{
+ size_t i;
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ int check = sdb_llist_append(list, &golden_data[i]);
+ fail_unless(check == 0,
+ "sdb_llist_append(%s) = %i; expected: 0",
+ golden_data[i].name, check);
+ }
+} /* populate */
+
+START_TEST(test_clone)
+{
+ sdb_llist_t *clone;
+ size_t i;
+
+ populate();
+
+ clone = sdb_llist_clone(list);
+ fail_unless(clone != NULL,
+ "sdb_llist_clone() = NULL; expected: cloned list object");
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ fail_unless(golden_data[i].ref_cnt == 3,
+ "sdb_llist_clone() did not take ownership");
+ }
+
+ sdb_llist_destroy(clone);
+}
+END_TEST
+
+START_TEST(test_destroy)
+{
+ size_t i;
+ populate();
+ sdb_llist_destroy(list);
+ list = NULL;
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ fail_unless(golden_data[i].ref_cnt == 1,
+ "sdb_llist_destroy() did not deref element %s",
+ golden_data[i].name);
+ }
+}
+END_TEST
+
+START_TEST(test_clear)
+{
+ size_t i;
+ populate();
+ sdb_llist_clear(list);
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ fail_unless(golden_data[i].ref_cnt == 1,
+ "sdb_llist_clear() did not deref element %s",
+ golden_data[i].name);
+ }
+
+ i = sdb_llist_len(list);
+ fail_unless(i == 0,
+ "sdb_llist_clear() left %zu elements in the list; "
+ "expected: 0", i);
+}
+END_TEST
+
+START_TEST(test_append)
+{
+ size_t i;
+
+ fail_unless(sdb_llist_len(list) == 0,
+ "sdb_llist_len(<empty list>) = %zu; expected: 0",
+ sdb_llist_len(list));
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ int check = sdb_llist_append(list, &golden_data[i]);
+ fail_unless(check == 0,
+ "sdb_llist_append(%s) = %i; expected: 0",
+ golden_data[i].name, check);
+ fail_unless(golden_data[i].ref_cnt == 2,
+ "sdb_llist_append(%s) did not take ownership",
+ golden_data[i].name);
+ fail_unless(sdb_llist_len(list) == i + 1,
+ "sdb_llist_len(<empty list>) = %zu; expected: zu",
+ sdb_llist_len(list), i + 1);
+ }
+}
+END_TEST
+
+START_TEST(test_insert)
+{
+ size_t i;
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ int check = sdb_llist_insert(list, &golden_data[i], 0);
+ fail_unless(check == 0,
+ "sdb_llist_insert(%s, 0) = %i; expected: 0",
+ golden_data[i].name, check);
+ fail_unless(golden_data[i].ref_cnt == 2,
+ "sdb_llist_insert(%s, 0) did not take ownership",
+ golden_data[i].name);
+ }
+}
+END_TEST
+
+START_TEST(test_validate_insert)
+{
+ size_t i;
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ /* none of these operations will succeed
+ * => 1 is invalid for each case */
+ int check = sdb_llist_insert(list, &golden_data[i], 1);
+ fail_unless(check == -1,
+ "sdb_llist_insert(%s, 1) = %i; expected: -1",
+ golden_data[i].name, check);
+ fail_unless(golden_data[i].ref_cnt == 1,
+ "sdb_llist_insert(%s, 1) took ownership",
+ golden_data[i].name);
+ }
+}
+END_TEST
+
+START_TEST(test_get)
+{
+ size_t i;
+ populate();
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ sdb_object_t *check = sdb_llist_get(list, i);
+ fail_unless(check == &golden_data[i],
+ "sdb_llist_get() = %p; expected: %p",
+ check, &golden_data[i]);
+ fail_unless(check->ref_cnt == 3,
+ "sdb_llist_get() didn't increment reference count; got: %i; "
+ "expected: 3", check->ref_cnt);
+ sdb_object_deref(check);
+ }
+}
+END_TEST
+
+START_TEST(test_remove_by_name)
+{
+ /* "random" indexes */
+ int indexes[] = { 4, 5, 3, 6, 2, 0, 1 };
+ size_t i;
+
+ populate();
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(indexes); ++i) {
+ sdb_object_t *check;
+
+ fail_unless((size_t)indexes[i] < SDB_STATIC_ARRAY_LEN(golden_data),
+ "INTERNAL ERROR: invalid index %i", indexes[i]);
+
+ check = sdb_llist_remove_by_name(list, golden_data[indexes[i]].name);
+ fail_unless(check == &golden_data[indexes[i]],
+ "sdb_llist_remove_by_name() = %p; expected: %p",
+ check, &golden_data[indexes[i]]);
+ fail_unless(check->ref_cnt == 2,
+ "sdb_llist_remove_by_name() returned unexpected reference "
+ "count; got: %i; expected: 2", check->ref_cnt);
+
+ check = sdb_llist_remove_by_name(list, golden_data[indexes[i]].name);
+ fail_unless(check == NULL,
+ "sdb_llist_remove_by_name() did not remove the element");
+ }
+}
+END_TEST
+
+static int
+dummy_lookup(const sdb_object_t __attribute__((unused)) *obj,
+ const void __attribute__((unused)) *user_data)
+{
+ return 0;
+} /* dummy_lookup */
+
+START_TEST(test_search)
+{
+ size_t i;
+ populate();
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ sdb_object_t *check = sdb_llist_search_by_name(list,
+ golden_data[i].name);
+ fail_unless(check == &golden_data[i],
+ "sdb_llist_search_by_name(%s) = NULL; expected: %p",
+ golden_data[i].name, &golden_data[i]);
+ }
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(unused_names); ++i) {
+ sdb_object_t *check = sdb_llist_search_by_name(list,
+ unused_names[i]);
+ fail_unless(check == NULL,
+ "sdb_llist_search_by_name(%s) = %p; expected: NULL",
+ unused_names[i], check);
+ }
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ /* dummy_lookup always return 0, thus, this will always return the
+ * first element */
+ sdb_object_t *check = sdb_llist_search(list, dummy_lookup, NULL);
+ fail_unless(check == &golden_data[i],
+ "sdb_llist_search() = %p (%s); expected: %p (%s)",
+ check, check->name, &golden_data[i], golden_data[i].name);
+
+ /* => remove the first element */
+ check = sdb_llist_remove(list, dummy_lookup, NULL);
+ fail_unless(check == &golden_data[i],
+ "sdb_llist_remove() = %p (%s); expected: %p (%s)",
+ check, check->name, &golden_data[i], golden_data[i].name);
+ fail_unless(check->ref_cnt == 2,
+ "sdb_llist_remove() changed reference count; got: %i; "
+ "expected: 2", check->ref_cnt);
+ }
+ /* should now be empty */
+ fail_unless(sdb_llist_len(list) == 0,
+ "Still have %i elements in the list; expected: 0",
+ sdb_llist_len(list));
+}
+END_TEST
+
+START_TEST(test_shift)
+{
+ size_t i;
+ populate();
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ sdb_object_t *check = sdb_llist_shift(list);
+ fail_unless(check == &golden_data[i],
+ "sdb_llist_shift() = NULL; expected: %p",
+ &golden_data[i]);
+ fail_unless(check->ref_cnt == 2,
+ "sdb_llist_shift() changed reference count; got: %i; "
+ "expected: 2", check->ref_cnt);
+ }
+
+ /* must be empty now */
+ fail_unless(sdb_llist_shift(list) == NULL,
+ "sdb_llist_shift() returned value; expected: NULL");
+}
+END_TEST
+
+START_TEST(test_iter)
+{
+ sdb_llist_iter_t *iter;
+ size_t i;
+
+ populate();
+
+ iter = sdb_llist_get_iter(list);
+ fail_unless(iter != NULL,
+ "sdb_llist_get_iter() did not return an iterator");
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ sdb_object_t *check;
+ fail_unless(sdb_llist_iter_has_next(iter),
+ "sdb_llist_iter_has_next() = FALSE; expected: TRUE");
+ check = sdb_llist_iter_get_next(iter);
+ fail_unless(check == &golden_data[i],
+ "sdb_llist_iter_get_next() = %p; expected: %p",
+ check, &golden_data[i]);
+ }
+
+ fail_unless(!sdb_llist_iter_has_next(iter),
+ "sdb_llist_iter_has_next() = TRUE; expected: FALSE");
+ fail_unless(sdb_llist_iter_get_next(iter) == NULL,
+ "sdb_llist_iter_get_next() returned value; expected: NULL");
+ sdb_llist_iter_destroy(iter);
+}
+END_TEST
+
+START_TEST(test_iter_remove)
+{
+ sdb_llist_iter_t *iter;
+ sdb_object_t *check;
+ size_t i;
+
+ populate();
+
+ iter = sdb_llist_get_iter(list);
+ fail_unless(iter != NULL,
+ "sdb_llist_get_iter() did not return an iterator");
+
+ i = 0;
+ while (sdb_llist_iter_has_next(iter)) {
+ check = sdb_llist_iter_get_next(iter);
+ fail_unless(check == &golden_data[i],
+ "sdb_llist_iter_get_next() = %p; expected: %p",
+ check, &golden_data[i]);
+
+ sdb_llist_iter_remove_current(iter);
+ ++i;
+ }
+ sdb_llist_iter_destroy(iter);
+
+ fail_unless(i == (size_t)SDB_STATIC_ARRAY_LEN(golden_data),
+ "iterated for %zu steps; expected: %i",
+ i, SDB_STATIC_ARRAY_LEN(golden_data));
+
+ /* all elements should be removed */
+ check = sdb_llist_shift(list);
+ fail_unless(check == NULL,
+ "sdb_llist_shift() = %p; expected: NULL", check);
+}
+END_TEST
+
+Suite *
+util_llist_suite(void)
+{
+ Suite *s = suite_create("utils::llist");
+ TCase *tc;
+
+ tc = tcase_create("core");
+ tcase_add_checked_fixture(tc, setup, teardown);
+ tcase_add_test(tc, test_clone);
+ tcase_add_test(tc, test_destroy);
+ tcase_add_test(tc, test_clear);
+ tcase_add_test(tc, test_append);
+ tcase_add_test(tc, test_insert);
+ tcase_add_test(tc, test_validate_insert);
+ tcase_add_test(tc, test_get);
+ tcase_add_test(tc, test_remove_by_name);
+ tcase_add_test(tc, test_search);
+ tcase_add_test(tc, test_shift);
+ tcase_add_test(tc, test_iter);
+ tcase_add_test(tc, test_iter_remove);
+ suite_add_tcase(s, tc);
+
+ return s;
+} /* util_llist_suite */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/t/unit/utils/strbuf_test.c b/t/unit/utils/strbuf_test.c
--- /dev/null
@@ -0,0 +1,564 @@
+/*
+ * SysDB - t/unit/utils/strbuf_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 "utils/strbuf.h"
+#include "libsysdb_test.h"
+
+#include <check.h>
+
+/*
+ * private data types
+ */
+
+static sdb_strbuf_t *buf;
+
+static void
+setup(void)
+{
+ buf = sdb_strbuf_create(0);
+ fail_unless(buf != NULL,
+ "sdb_strbuf_create() = NULL; expected strbuf object");
+} /* setup */
+
+static void
+teardown(void)
+{
+ sdb_strbuf_destroy(buf);
+ buf = NULL;
+} /* teardown */
+
+/*
+ * tests
+ */
+
+START_TEST(test_null)
+{
+ sdb_strbuf_t *b = NULL;
+ va_list ap;
+
+ /* check that methods don't crash */
+ sdb_strbuf_destroy(b);
+ sdb_strbuf_skip(b, 0, 0);
+ sdb_strbuf_skip(b, 0, 10);
+ sdb_strbuf_skip(b, 10, 10);
+ sdb_strbuf_clear(b);
+
+ /* check that methods return an error */
+ fail_unless(sdb_strbuf_vappend(b, "test", ap) < 0,
+ "sdb_strbuf_vappend(NULL) didn't report failure");
+ fail_unless(sdb_strbuf_append(b, "test") < 0,
+ "sdb_strbuf_append(NULL) didn't report failure");
+ fail_unless(sdb_strbuf_vsprintf(b, "test", ap) < 0,
+ "sdb_strbuf_vsprintf(NULL) didn't report failure");
+ fail_unless(sdb_strbuf_sprintf(b, "test") < 0,
+ "sdb_strbuf_sprintf(NULL) didn't report failure");
+ fail_unless(sdb_strbuf_memcpy(b, "test", 4) < 0,
+ "sdb_strbuf_memcpy(NULL) didn't report failure");
+ fail_unless(sdb_strbuf_memappend(b, "test", 4) < 0,
+ "sdb_strbuf_memappend(NULL) didn't report failure");
+ fail_unless(sdb_strbuf_read(b, 0, 32) < 0,
+ "sdb_strbuf_read(NULL) didn't report failure");
+ fail_unless(sdb_strbuf_chomp(b) < 0,
+ "sdb_strbuf_chomp(NULL) didn't report failure");
+}
+END_TEST
+
+START_TEST(test_empty)
+{
+ sdb_strbuf_t *b = sdb_strbuf_create(0);
+ const char *data;
+ size_t len;
+
+ /* check that methods don't crash */
+ sdb_strbuf_skip(b, 1, 1);
+ sdb_strbuf_clear(b);
+ sdb_strbuf_chomp(b);
+
+ data = sdb_strbuf_string(b);
+ fail_unless(data && (*data == '\0'),
+ "sdb_strbuf_string(<empty>) = '%s'; expected: ''", data);
+ len = sdb_strbuf_len(b);
+ fail_unless(len == 0,
+ "sdb_strbuf_len(<empty>) = %zu; expected: 0", len);
+
+ sdb_strbuf_destroy(b);
+}
+END_TEST
+
+START_TEST(test_sdb_strbuf_create)
+{
+ sdb_strbuf_t *s;
+ size_t len;
+
+ s = sdb_strbuf_create(0);
+ fail_unless(s != NULL,
+ "sdb_strbuf_create() = NULL; expected strbuf object");
+ len = sdb_strbuf_len(s);
+ fail_unless(len == 0,
+ "sdb_strbuf_create() created buffer with len = %zu; "
+ "expected: 0", len);
+ sdb_strbuf_destroy(s);
+
+ s = sdb_strbuf_create(128);
+ fail_unless(s != NULL,
+ "sdb_strbuf_create() = NULL; expected strbuf object");
+ len = sdb_strbuf_len(s);
+ /* len still has to be 0 -- there's no content */
+ fail_unless(len == 0,
+ "sdb_strbuf_create() created buffer with len = %zu; "
+ "expected: 0", len);
+ sdb_strbuf_destroy(s);
+}
+END_TEST
+
+START_TEST(test_sdb_strbuf_append)
+{
+ ssize_t n, expected;
+ size_t len;
+ const char *test;
+
+ n = sdb_strbuf_append(buf, "1234567890");
+ fail_unless(n == 10,
+ "sdb_strbuf_append() appended %zi bytes; expected: 10", n);
+ len = sdb_strbuf_len(buf);
+ fail_unless(len == 10,
+ "sdb_strbuf_append() left behind buffer with len = %zu; "
+ "expected: 10", len);
+
+ n = sdb_strbuf_append(buf, "ABCDE");
+ fail_unless(n == 5,
+ "sdb_strbuf_append() appended %zi bytes; expected: 5", n);
+ len = sdb_strbuf_len(buf);
+ fail_unless(len == 15,
+ "sdb_strbuf_append() left behind buffer with len = %zu; "
+ "expected: 15", len);
+
+ test = sdb_strbuf_string(buf);
+ fail_unless(test[len] == '\0',
+ "sdb_strbuf_append() did not nil-terminate the string");
+ fail_unless(!strcmp(test, "1234567890ABCDE"),
+ "sdb_strbuf_append() did not correctly concatenate two string; "
+ "got: %s; expected: 1234567890ABCDE", test);
+
+ n = sdb_strbuf_append(buf, "%zu; %5.4f", len, (double)len / 10.0);
+ expected = /* len */ 2 + /* "; " */ 2 + /* decimal len/10 */ 6;
+ fail_unless(n == expected,
+ "sdb_strbuf_append() appended %zi bytes; expected: %zi",
+ n, expected);
+ len = sdb_strbuf_len(buf);
+ fail_unless(len == 15 + (size_t)expected,
+ "sdb_strbuf_append() left behind buffer with len = %zu; "
+ "expected: %zu", len, 15 + (size_t)expected);
+
+ test = sdb_strbuf_string(buf);
+ fail_unless(test[len] == '\0',
+ "sdb_strbuf_append() did not nil-terminate the string");
+ fail_unless(!strcmp(test, "1234567890ABCDE15; 1.5000"),
+ "sdb_strbuf_append() did not correctly concatenate two string; "
+ "got: %s; expected: 1234567890ABCDE15; 1.5000", test);
+}
+END_TEST
+
+START_TEST(test_sdb_strbuf_sprintf)
+{
+ ssize_t n, expected;
+ size_t len;
+ const char *test;
+
+ n = sdb_strbuf_sprintf(buf, "1234567890");
+ fail_unless(n == 10,
+ "sdb_strbuf_sprintf() wrote %zi bytes; expected: 10", n);
+ len = sdb_strbuf_len(buf);
+ fail_unless(len == 10,
+ "sdb_strbuf_sprintf() left behind buffer with len = %zu; "
+ "expected: 10", len);
+
+ n = sdb_strbuf_sprintf(buf, "ABCDE");
+ fail_unless(n == 5,
+ "sdb_strbuf_sprintf() wrote %zi bytes; expected: 5", n);
+ len = sdb_strbuf_len(buf);
+ fail_unless(len == 5,
+ "sdb_strbuf_sprintf() left behind buffer with len = %zu; "
+ "expected: 5", len);
+
+ test = sdb_strbuf_string(buf);
+ fail_unless(test[len] == '\0',
+ "sdb_strbuf_sprintf() did not nil-terminate the string");
+ fail_unless(!strcmp(test, "ABCDE"),
+ "sdb_strbuf_sprintf() did not format string correctly; "
+ "got: %s; expected: ABCDE", test);
+
+ n = sdb_strbuf_sprintf(buf, "%zu; %5.4f", len, (double)len / 10.0);
+ expected = /* len */ 1 + /* "; " */ 2 + /* decimal len/10 */ 6;
+ fail_unless(n == expected,
+ "sdb_strbuf_sprintf() wrote %zi bytes; expected: %zi",
+ n, expected);
+ len = sdb_strbuf_len(buf);
+ fail_unless(len == (size_t)expected,
+ "sdb_strbuf_sprintf() left behind buffer with len = %zu; "
+ "expected: %zu", len, (size_t)expected);
+
+ test = sdb_strbuf_string(buf);
+ fail_unless(test[len] == '\0',
+ "sdb_strbuf_sprintf() did not nil-terminate the string");
+ fail_unless(!strcmp(test, "5; 0.5000"),
+ "sdb_strbuf_sprintf() did not format string correctly; "
+ "got: %s; expected: 5; 0.5000", test);
+}
+END_TEST
+
+START_TEST(test_incremental)
+{
+ ssize_t n;
+ size_t i;
+
+ sdb_strbuf_destroy(buf);
+ buf = sdb_strbuf_create(1024);
+
+ /* fill buffer one by one; leave room for nul-byte */
+ for (i = 0; i < 1023; ++i) {
+ n = sdb_strbuf_append(buf, ".");
+ fail_unless(n == 1, "sdb_strbuf_append() = %zi; expected: 1", n);
+ }
+
+ /* write another byte; this has to trigger a resize */
+ n = sdb_strbuf_append(buf, ".");
+ fail_unless(n == 1, "sdb_strbuf_append() = %zi; expected: 1", n);
+
+ /* write more bytes; this should trigger at least one more resize but
+ * that's an implementation detail */
+ for (i = 0; i < 1024; ++i) {
+ n = sdb_strbuf_append(buf, ".");
+ fail_unless(n == 1, "sdb_strbuf_append() = %zi; expected: 1", n);
+ }
+
+ n = (ssize_t)sdb_strbuf_len(buf);
+ fail_unless(n == 2048, "sdb_strbuf_len() = %zi; expectd: 2048", n);
+}
+END_TEST
+
+static struct {
+ const char *input;
+ size_t size;
+} mem_golden_data[] = {
+ { "abc\0\x10\x42", 6 },
+ { "\0\1\2\3\4", 5 },
+ { "\n\n\0\n\n", 5 },
+ { "", 0 },
+};
+
+START_TEST(test_sdb_strbuf_memcpy)
+{
+ size_t i;
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(mem_golden_data); ++i) {
+ ssize_t n;
+ const char *check;
+
+ n = sdb_strbuf_memcpy(buf, mem_golden_data[i].input,
+ mem_golden_data[i].size);
+ fail_unless(n >= 0,
+ "sdb_strbuf_memcpy() = %zi; expected: >=0", n);
+ fail_unless((size_t)n == mem_golden_data[i].size,
+ "sdb_strbuf_memcpy() = %zi; expected: %zu",
+ n, mem_golden_data[i].size);
+
+ n = (ssize_t)sdb_strbuf_len(buf);
+ fail_unless((size_t)n == mem_golden_data[i].size,
+ "sdb_strbuf_len() = %zu (after memcpy); expected: %zu",
+ n, mem_golden_data[i].size);
+
+ check = sdb_strbuf_string(buf);
+ fail_unless(check != NULL,
+ "sdb_strbuf_string() = NULL (after memcpy); expected: data");
+ fail_unless(check[mem_golden_data[i].size] == '\0',
+ "sdb_strbuf_memcpy() did not nil-terminate the data");
+ fail_unless(!memcmp(check, mem_golden_data[i].input,
+ mem_golden_data[i].size),
+ "sdb_strbuf_memcpy() did not set the buffer correctly");
+ }
+}
+END_TEST
+
+START_TEST(test_sdb_strbuf_memappend)
+{
+ size_t i;
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(mem_golden_data); ++i) {
+ ssize_t n;
+ const char *check;
+
+ size_t total, j;
+
+ n = sdb_strbuf_memappend(buf, mem_golden_data[i].input,
+ mem_golden_data[i].size);
+ fail_unless(n >= 0,
+ "sdb_strbuf_memappend() = %zi; expected: >=0", n);
+ fail_unless((size_t)n == mem_golden_data[i].size,
+ "sdb_strbuf_memappend() = %zi; expected: %zu",
+ n, mem_golden_data[i].size);
+
+ check = sdb_strbuf_string(buf);
+ fail_unless(check != NULL,
+ "sdb_strbuf_string() = NULL (after memappend); "
+ "expected: data");
+
+ n = (ssize_t)sdb_strbuf_len(buf);
+ total = 0;
+ for (j = 0; j <= i; ++j) {
+ fail_unless(total + mem_golden_data[j].size <= (size_t)n,
+ "sdb_strbuf_len() = %zu (after memappend); "
+ "expected: >=%zu", n, total + mem_golden_data[j].size);
+
+ fail_unless(!memcmp(check + total, mem_golden_data[j].input,
+ mem_golden_data[j].size),
+ "sdb_strbuf_memappend() did not "
+ "set the buffer correctly");
+ total += mem_golden_data[j].size;
+ }
+ fail_unless((size_t)n == total,
+ "sdb_strbuf_len() = %zu (after memappend); expected: %zu",
+ n, total);
+
+ fail_unless(check[total] == '\0',
+ "sdb_strbuf_memappend() did not nil-terminate the data");
+ }
+}
+END_TEST
+
+static struct {
+ const char *input;
+ ssize_t expected;
+ const char *expected_string;
+} chomp_golden_data[] = {
+ { NULL, 0, "" },
+ { "\n", 1, "" },
+ { "\n\n", 2, "" },
+ { "12345\n\n\n", 3, "12345" },
+ { "abcd", 0, "abcd" },
+};
+
+START_TEST(test_sdb_strbuf_chomp)
+{
+ size_t i;
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(chomp_golden_data); ++i) {
+ ssize_t n;
+ const char *check;
+
+ if (chomp_golden_data[i].input)
+ sdb_strbuf_sprintf(buf, chomp_golden_data[i].input);
+
+ /* empty buffer */
+ n = sdb_strbuf_chomp(buf);
+ fail_unless(n == chomp_golden_data[i].expected,
+ "sdb_strbuf_chomp() = %zi; expected: %zi", n,
+ chomp_golden_data[i].expected);
+
+ check = sdb_strbuf_string(buf);
+ fail_unless(!strcmp(check, chomp_golden_data[i].expected_string),
+ "sdb_strbuf_chomp() did not correctly remove newlines; "
+ "got string '%s'; expected: '%s'", check,
+ chomp_golden_data[i].expected_string);
+ }
+}
+END_TEST
+
+/* input is "1234567890" */
+static struct {
+ size_t offset;
+ size_t n;
+ const char *expected;
+ size_t expected_len;
+} skip_golden_data[] = {
+ { 0, 0, "1234567890", 10 },
+ { 0, 1, "234567890", 9 },
+ { 0, 2, "34567890", 8 },
+ { 0, 9, "0", 1 },
+ { 0, 10, "", 0 },
+ { 0, 11, "", 0 },
+ { 0, 100, "", 0 },
+ { 1, 0, "1234567890", 10 },
+ { 1, 1, "134567890", 9 },
+ { 1, 2, "14567890", 8 },
+ { 2, 0, "1234567890", 10 },
+ { 2, 1, "124567890", 9 },
+ { 2, 2, "12567890", 8 },
+ { 2, 3, "1267890", 7 },
+ { 2, 4, "127890", 6 },
+ { 2, 5, "12890", 5 },
+ { 2, 6, "1290", 4 },
+ { 2, 7, "120", 3 },
+ { 2, 8, "12", 2 },
+ { 2, 9, "12", 2 },
+ { 2, 10, "12", 2 },
+ { 8, 1, "123456780", 9 },
+ { 8, 2, "12345678", 8 },
+ { 8, 3, "12345678", 8 },
+ { 9, 1, "123456789", 9 },
+ { 9, 2, "123456789", 9 },
+ { 10, 1, "1234567890", 10 },
+ { 10, 2, "1234567890", 10 },
+};
+
+START_TEST(test_sdb_strbuf_skip)
+{
+ const char *input = "1234567890";
+ size_t i;
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(skip_golden_data); ++i) {
+ const char *check;
+ size_t n;
+
+ sdb_strbuf_sprintf(buf, input);
+ sdb_strbuf_skip(buf, skip_golden_data[i].offset,
+ skip_golden_data[i].n);
+
+ n = sdb_strbuf_len(buf);
+ fail_unless(n == skip_golden_data[i].expected_len,
+ "sdb_strbuf_len() = %zu (after skip); expected: %zu",
+ n, skip_golden_data[i].expected_len);
+
+ check = sdb_strbuf_string(buf);
+ fail_unless(!!check,
+ "sdb_strbuf_string() = NULL (after skip); expected: string");
+
+ fail_unless(check[n] == '\0',
+ "sdb_strbuf_skip() did not nil-terminate the string");
+
+ fail_unless(! strcmp(skip_golden_data[i].expected, check),
+ "sdb_strbuf_skip('%s', %zu) did not skip correctly; "
+ "got string '%s'; expected: '%s'", input,
+ skip_golden_data[i].n, check, skip_golden_data[i].expected);
+ }
+}
+END_TEST
+
+START_TEST(test_sdb_strbuf_clear)
+{
+ const char *data;
+ size_t len;
+
+ sdb_strbuf_append(buf, "abc");
+ len = sdb_strbuf_len(buf);
+ fail_unless(len != 0,
+ "sdb_strbuf_len() = %zu; expected: != 0", len);
+
+ sdb_strbuf_clear(buf);
+ len = sdb_strbuf_len(buf);
+ fail_unless(len == 0,
+ "sdb_strbuf_len() = %zu (after clear); expected: 0", len);
+
+ data = sdb_strbuf_string(buf);
+ fail_unless(*data == '\0',
+ "sdb_strbuf_string() = '%s' (after clear); expected: ''", data);
+}
+END_TEST
+
+static struct {
+ const char *input;
+ const char *expected;
+} string_golden_data[] = {
+ { NULL, "" },
+ { "a", "a" },
+ { "abcdef", "abcdef" },
+};
+
+START_TEST(test_sdb_strbuf_string)
+{
+ size_t i;
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(string_golden_data); ++i) {
+ const char *check;
+
+ if (string_golden_data[i].input)
+ sdb_strbuf_sprintf(buf, string_golden_data[i].input);
+ check = sdb_strbuf_string(buf);
+ fail_unless(!strcmp(check, string_golden_data[i].expected),
+ "sdb_strbuf_string() = '%s'; expected: '%s'",
+ check, string_golden_data[i].expected);
+ }
+}
+END_TEST
+
+static struct {
+ const char *input;
+ size_t expected;
+} len_golden_data[] = {
+ { NULL, 0 },
+ { "a", 1 },
+ { "12345", 5 },
+};
+
+START_TEST(test_sdb_strbuf_len)
+{
+ size_t i;
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(len_golden_data); ++i) {
+ size_t check;
+
+ if (len_golden_data[i].input)
+ sdb_strbuf_sprintf(buf, len_golden_data[i].input);
+ check = sdb_strbuf_len(buf);
+ fail_unless(check == len_golden_data[i].expected,
+ "sdb_strbuf_len() = %zu; expected: %zu",
+ check, len_golden_data[i].expected);
+ }
+}
+END_TEST
+
+Suite *
+util_strbuf_suite(void)
+{
+ Suite *s = suite_create("utils::strbuf");
+ TCase *tc;
+
+ tc = tcase_create("empty");
+ tcase_add_test(tc, test_null);
+ tcase_add_test(tc, test_empty);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("core");
+ tcase_add_checked_fixture(tc, setup, teardown);
+ tcase_add_test(tc, test_sdb_strbuf_create);
+ tcase_add_test(tc, test_sdb_strbuf_append);
+ tcase_add_test(tc, test_sdb_strbuf_sprintf);
+ tcase_add_test(tc, test_incremental);
+ tcase_add_test(tc, test_sdb_strbuf_memcpy);
+ tcase_add_test(tc, test_sdb_strbuf_memappend);
+ tcase_add_test(tc, test_sdb_strbuf_chomp);
+ tcase_add_test(tc, test_sdb_strbuf_skip);
+ tcase_add_test(tc, test_sdb_strbuf_clear);
+ tcase_add_test(tc, test_sdb_strbuf_string);
+ tcase_add_test(tc, test_sdb_strbuf_len);
+ suite_add_tcase(s, tc);
+
+ return s;
+} /* util_strbuf_suite */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/t/unit/utils/unixsock_test.c b/t/unit/utils/unixsock_test.c
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ * SysDB - t/unit/utils/unixsock_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.
+ */
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+/* required for fopencookie support */
+#define _GNU_SOURCE
+
+#include "utils/unixsock.h"
+#include "libsysdb_test.h"
+
+#include <check.h>
+
+#include <dlfcn.h>
+
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <unistd.h>
+
+/*
+ * I/O "hook" functions
+ */
+
+typedef struct {
+ int fd;
+ size_t pos;
+} io_cookie_t;
+
+static struct {
+ const char *data;
+ size_t len;
+} golden_data[] = {
+ { "a", 1 },
+ { "abc", 3 },
+ { "12345", 5 },
+ { "", 0 },
+};
+
+static char *last_write = NULL;
+
+static unsigned long long mock_read_called = 0;
+static ssize_t
+mock_read(void *cookie, char *buf, size_t size)
+{
+ io_cookie_t *c = cookie;
+ ssize_t ret;
+
+ ++mock_read_called;
+
+ if (c->pos >= SDB_STATIC_ARRAY_LEN(golden_data))
+ return 0;
+
+ ret = snprintf(buf, size, "%s\n", golden_data[c->pos].data);
+ ++c->pos;
+ return ret;
+} /* mock_read */
+
+static unsigned long long mock_write_called = 0;
+static ssize_t
+mock_write(void *cookie, const char *buf, size_t size)
+{
+ io_cookie_t *c = cookie;
+
+ ++mock_write_called;
+
+ if (c->pos >= SDB_STATIC_ARRAY_LEN(golden_data))
+ return 0;
+
+ if (last_write)
+ free(last_write);
+ last_write = strdup(buf);
+ ++c->pos;
+ return (ssize_t)size;
+} /* mock_write */
+
+/* unsupported: int seek(void *cookie, off64_t *offset, int whence) */
+
+static int
+mock_close(void *cookie)
+{
+ io_cookie_t *c = cookie;
+
+ if (! c)
+ return EBADF;
+
+ close(c->fd);
+ free(c);
+ return 0;
+} /* mock_close */
+
+static cookie_io_functions_t mock_io = {
+ /* read = */ mock_read,
+ /* write = */ mock_write,
+ /* seek = */ NULL,
+ /* close = */ mock_close,
+};
+
+/*
+ * mocked functions
+ */
+
+static int myfd = -1;
+
+static void *
+dlopen_libc(void)
+{
+ static void *libc = NULL;
+
+ if (libc)
+ return libc;
+
+ libc = dlopen("libc.so.6", RTLD_LAZY);
+ if (! libc)
+ fail("INTERNAL ERROR: failed to load libc");
+ return libc;
+} /* dlopen_libc */
+
+int
+socket(int domain, int __attribute__((unused)) type,
+ int __attribute__((unused)) protocol)
+{
+ char tmp_file[] = "unixsock_test_socket.XXXXXX";
+ int tmp_fd;
+
+ /* we only want to mock UNIX sockets; return an error else */
+ if (domain != AF_UNIX) {
+ errno = EAFNOSUPPORT;
+ return -1;
+ }
+
+ /* create an 'anonymous' file to have a valid file-descriptor
+ * which can be close()ed by the caller */
+ tmp_fd = mkstemp(tmp_file);
+ if (tmp_fd < 0)
+ return -1;
+
+ unlink(tmp_file);
+ myfd = tmp_fd;
+ return tmp_fd;
+} /* socket */
+
+int
+connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
+{
+ if (sockfd < 0) {
+ errno = EBADF;
+ return -1;
+ }
+
+ /* we only want to mock UNIX sockets; return an error else */
+ if ((addrlen != sizeof(struct sockaddr_un)) || (! addr)
+ || (! ((const struct sockaddr_un *)addr)->sun_path)) {
+ errno = EAFNOSUPPORT;
+ return -1;
+ }
+ return 0;
+} /* connect */
+
+FILE *
+fdopen(int fd, const char *mode)
+{
+ io_cookie_t *cookie;
+
+ if (fd < 0) {
+ errno = EBADF;
+ return NULL;
+ }
+
+ /* check also uses fdopen; in that case we need
+ * to use the original implementation */
+ if (fd != myfd) {
+ void *libc = dlopen_libc();
+ FILE *(*orig_fdopen)(int, const char *);
+
+ orig_fdopen = (FILE *(*)(int, const char *))dlsym(libc, "fdopen");
+
+ if (! orig_fdopen)
+ fail("INTERNAL ERROR: failed to load fdopen() from libc");
+
+ return orig_fdopen(fd, mode);
+ }
+
+ cookie = calloc(sizeof(*cookie), 1);
+ if (! cookie)
+ return NULL;
+
+ cookie->fd = fd;
+ cookie->pos = 0;
+ return fopencookie(cookie, mode, mock_io);
+} /* fdopen */
+
+/*
+ * private variables
+ */
+
+static sdb_unixsock_client_t *client;
+
+static void
+setup(void)
+{
+ client = sdb_unixsock_client_create("unixsock_test_path");
+ fail_unless(client != NULL,
+ "sdb_unixsock_client_create() = NULL; "
+ "expected unixsock client object");
+} /* setup */
+
+static void
+teardown(void)
+{
+ sdb_unixsock_client_destroy(client);
+ client = NULL;
+} /* teardown */
+
+static void
+conn(void)
+{
+ int check;
+
+ check = sdb_unixsock_client_connect(client);
+ fail_unless(check == 0,
+ "sdb_unixsock_client_connect() = %i; expected: 0", check);
+} /* conn */
+
+/*
+ * tests
+ */
+
+START_TEST(test_sdb_unixsock_client_create)
+{
+ sdb_unixsock_client_t *c;
+ const char *check;
+
+ c = sdb_unixsock_client_create(NULL);
+ fail_unless(c == NULL,
+ "sdb_unixsock_client_create() = %p; expected: NULL", c);
+
+ c = sdb_unixsock_client_create("unixsock_test_path");
+ fail_unless(c != NULL,
+ "sdb_unixsock_client_create() = NULL; "
+ "expected unixsock client object");
+
+ check = sdb_unixsock_client_path(c);
+ fail_unless(check != NULL,
+ "sdb_unixsock_client_create() did not store path name");
+ fail_unless(!strcmp(check, "unixsock_test_path"),
+ "sdb_unixsock_client_create() did not store correct path name; "
+ "got: '%s'; expected: 'unixsock_test_path'", check);
+ sdb_unixsock_client_destroy(c);
+}
+END_TEST
+
+START_TEST(test_sdb_unixsock_client_connect)
+{
+ int check;
+
+ check = sdb_unixsock_client_connect(client);
+ fail_unless(check == 0,
+ "sdb_unixsock_client_connect() = %i; expected: 0", check);
+}
+END_TEST
+
+START_TEST(test_sdb_unixsock_client_send)
+{
+ size_t i;
+
+ conn();
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ int check;
+
+ mock_write_called = 0;
+ check = sdb_unixsock_client_send(client, golden_data[i].data);
+ /* client_send appends \r\n */
+ fail_unless((size_t)check == golden_data[i].len + 2,
+ "sdb_unixsock_client_send() = %i; expected: %zu",
+ check, golden_data[i].len + 2);
+ fail_unless(mock_write_called == 1,
+ "sdb_unixsock_client_send() called mock_write %llu times; "
+ "expected: 1", mock_write_called);
+ fail_unless(last_write != NULL,
+ "INTERNAL ERROR: mock_write did not record last write");
+ fail_unless((last_write[check - 1] == '\n')
+ && (last_write[check - 2] == '\r'),
+ "sdb_unixsock_client_send() did not append \\r\\n "
+ "before sending; got: '%s'", last_write);
+ fail_unless(!strncmp(last_write, golden_data[i].data,
+ (size_t)check - 2),
+ "sdb_unixsock_client_send() sent unexpected string '%s'; "
+ "expected: '%s'", last_write, golden_data[i].data);
+ free(last_write);
+ last_write = NULL;
+ }
+}
+END_TEST
+
+START_TEST(test_sdb_unixsock_client_recv)
+{
+ size_t i;
+
+ conn();
+
+ for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+ char *check;
+ char buf[64];
+
+ mock_read_called = 0;
+ check = sdb_unixsock_client_recv(client, buf, sizeof(buf));
+ fail_unless(check != NULL,
+ "sdb_unixsock_client_recv() = NULL; expected: a string");
+ fail_unless(check == buf,
+ "sdb_unixsock_client_recv() did not return a pointer "
+ "to the user-provided buffer");
+ fail_unless(mock_read_called == 1,
+ "sdb_unixsock_client_recv() called mock_read %llu times; "
+ "expected: 1", mock_read_called);
+ fail_unless(strlen(check) == golden_data[i].len,
+ "sdb_unixsock_client_recv() returned string of length "
+ "%zu ('%s'); expected: %zu",
+ strlen(check), check, golden_data[i].len);
+ fail_unless(check[golden_data[i].len] != '\n',
+ "sdb_unixsock_client_recv() did not strip newline");
+ fail_unless(!strcmp(check, golden_data[i].data),
+ "sdb_unixsock_client_recv() = '%s'; expected: '%s'",
+ check, golden_data[i].data);
+ }
+}
+END_TEST
+
+Suite *
+util_unixsock_suite(void)
+{
+ Suite *s = suite_create("utils::unixsock");
+ TCase *tc;
+
+ tc = tcase_create("core");
+ tcase_add_checked_fixture(tc, setup, teardown);
+ tcase_add_test(tc, test_sdb_unixsock_client_create);
+ tcase_add_test(tc, test_sdb_unixsock_client_connect);
+ tcase_add_test(tc, test_sdb_unixsock_client_send);
+ tcase_add_test(tc, test_sdb_unixsock_client_recv);
+ suite_add_tcase(s, tc);
+
+ return s;
+} /* util_unixsock_suite */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/t/utils/channel_test.c b/t/utils/channel_test.c
--- a/t/utils/channel_test.c
+++ /dev/null
@@ -1,372 +0,0 @@
-/*
- * SysDB - t/utils/channel_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 "utils/channel.h"
-#include "libsysdb_test.h"
-
-#include <check.h>
-#include <errno.h>
-#include <limits.h>
-
-#include <stdint.h>
-
-static struct {
- int data;
- int expected_write;
- int expected_read;
-} golden_data_int[] = {
- { 5, 0, 0 },
- { 15, 0, 0 },
- { -3, 0, 0 },
- { INT_MAX, 0, 0 },
- { 27, 0, 0 },
- { 42, 0, 0 },
- { 6, 0, 0 },
- { 2854, 0, 0 },
- { 10562, 0, 0 },
- { 0, 0, 0 },
-
- /* exceeding buffer size */
- { 20, -1, -1 },
- { 42, -1, -1 },
-};
-
-static struct {
- char *data;
- int expected_write;
- int expected_read;
-} golden_data_string[] = {
- { "c", 0, 0 },
- { "", 0, 0 },
- { "abc", 0, 0 },
- { "foobar", 0, 0 },
- { "qux", 0, 0 },
- { "a b c", 0, 0 },
- { "123", 0, 0 },
- { "xyz", 0, 0 },
- { "b", 0, 0 },
- { "a", 0, 0 },
-
- /* exceeding buffer size */
- { "err1", -1, -1 },
- { "err2", -1, -1 },
-};
-
-static sdb_channel_t *chan;
-
-static void
-setup_int(void)
-{
- chan = sdb_channel_create(10, sizeof(int));
- fail_unless(chan != NULL,
- "sdb_channel_create(10, sizeof(int)) = NULL; "
- "expected list object");
-} /* setup_int */
-
-static void
-setup_string(void)
-{
- chan = sdb_channel_create(10, sizeof(char *));
- fail_unless(chan != NULL,
- "sdb_chan_create(10, sizeof(char *))) = NULL; "
- "expected channel object");
-} /* setup_string */
-
-static void
-teardown(void)
-{
- sdb_channel_destroy(chan);
- chan = NULL;
-} /* teardown */
-
-START_TEST(test_create)
-{
- chan = sdb_channel_create(0, 0);
- fail_unless(chan == NULL,
- "sdb_channel_create(0, 0) = %p; expected: NULL", chan);
-
- chan = sdb_channel_create(0, 1);
- fail_unless(chan != NULL,
- "sdb_channel_create(0, 1) = NULL; expected: channel object");
- sdb_channel_destroy(chan);
-
- chan = sdb_channel_create(42, 23);
- fail_unless(chan != NULL,
- "sdb_channel_create(32, 23) = NULL; expected: channel object");
- sdb_channel_destroy(chan);
-}
-END_TEST
-
-START_TEST(test_write_read)
-{
- uint32_t data;
- int check;
-
- chan = sdb_channel_create(0, 1);
- fail_unless(chan != NULL,
- "sdb_channel_create(0, 0) = NULL; expected: channel object");
-
- data = 0x00ffff00;
- check = sdb_channel_write(chan, &data);
- fail_unless(!check, "sdb_channel_write() = %i; expected: 0", check);
- check = sdb_channel_write(chan, &data);
- fail_unless(check, "sdb_channel_write() = 0; expected: <0");
-
- data = 0xffffffff;
- check = sdb_channel_read(chan, &data);
- fail_unless(check == 0,
- "sdb_channel_read() = %d; expected: 0", check);
- /* result depends on endianess */
- fail_unless((data == 0xffffff00) || (data == 0x00ffffff),
- "sdb_channel_read() returned data %x; "
- "expected: 0xffffff00 || 0x00ffffff", data);
-
- sdb_channel_destroy(chan);
-}
-END_TEST
-
-START_TEST(test_select)
-{
- struct timespec ts = { 0, 10 };
- int check;
- int data;
-
- chan = sdb_channel_create(0, 1);
-
- errno = 0;
- check = sdb_channel_select(chan, &data, NULL, NULL, NULL, &ts);
- fail_unless(check < ETIMEDOUT,
- "sdb_channel_select() = %i; expected: <0", check);
- fail_unless(errno == ETIMEDOUT,
- "sdb_channel_select() set errno to %i; expected: %i (ETIMEDOUT)",
- errno, ETIMEDOUT);
-
- check = sdb_channel_select(chan, NULL, NULL, &data, NULL, NULL);
- fail_unless(! check, "sdb_channel_select() = %i; expected: 0", check);
- check = sdb_channel_select(chan, NULL, NULL, &data, NULL, &ts);
- fail_unless(! check, "sdb_channel_select() = %i; expected: 0", check);
-
- sdb_channel_destroy(chan);
-}
-END_TEST
-
-START_TEST(test_write_int)
-{
- size_t i;
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data_int); ++i) {
- int data = golden_data_int[i].data;
- int expected = golden_data_int[i].expected_write;
-
- int check = sdb_channel_write(chan, &data);
- fail_unless(check == expected,
- "sdb_channel_write(chan, %i) = %i; expected: %i",
- data, check, expected);
- }
-}
-END_TEST
-
-START_TEST(test_read_int)
-{
- size_t i;
-
- /* populate */
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data_int); ++i) {
- int data = golden_data_int[i].data;
- int expected = golden_data_int[i].expected_write;
-
- int check = sdb_channel_write(chan, &data);
- fail_unless(check == expected,
- "sdb_channel_write(chan, %i) = %i; expected: %i",
- data, check, expected);
- }
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data_int); ++i) {
- int data = (int)i;
- int expected = golden_data_int[i].expected_read;
-
- int check = sdb_channel_read(chan, &data);
- fail_unless(check == expected,
- "sdb_channel_read(chan, %i) = %i; expected: %i",
- data, check, expected);
- if (check) {
- fail_unless(data == (int)i,
- "sdb_channel_read() modified data to '%i'; "
- "expected: no modification", data);
- }
- else {
- fail_unless(data == golden_data_int[i].data,
- "sdb_channel_read() returned data %i; expected: %i",
- data, golden_data_int[i].data);
- }
- }
-}
-END_TEST
-
-START_TEST(test_write_read_int)
-{
- size_t i;
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data_int); ++i) {
- int data = golden_data_int[i].data;
- int check = sdb_channel_write(chan, &data);
- fail_unless(check == 0,
- "sdb_channel_write(chan, %i) = %i; expected: 0",
- data, check);
-
- data = (int)i;
- check = sdb_channel_read(chan, &data);
- fail_unless(check == 0,
- "sdb_channel_read(chan, %i) = %i; expected: 0",
- data, check);
- if (check) {
- fail_unless(data == (int)i,
- "sdb_channel_read() modified data to '%i'; "
- "expected: no modification", data);
- }
- else {
- fail_unless(data == golden_data_int[i].data,
- "sdb_channel_read() returned data %i; expected: %i",
- data, golden_data_int[i].data);
- }
- }
-}
-END_TEST
-
-START_TEST(test_write_string)
-{
- size_t i;
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data_string); ++i) {
- char *data = golden_data_string[i].data;
- int expected = golden_data_string[i].expected_write;
-
- int check = sdb_channel_write(chan, &data);
- fail_unless(check == expected,
- "sdb_channel_write(chan, '%s') = %i; expected: %i",
- data, check, expected);
- }
-}
-END_TEST
-
-START_TEST(test_read_string)
-{
- size_t i;
-
- /* populate */
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data_string); ++i) {
- char *data = golden_data_string[i].data;
- int expected = golden_data_string[i].expected_write;
-
- int check = sdb_channel_write(chan, &data);
- fail_unless(check == expected,
- "sdb_channel_write(chan, '%s') = %i; expected: %i",
- data, check, expected);
- }
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data_string); ++i) {
- char *data = NULL;
- int expected = golden_data_string[i].expected_read;
-
- int check = sdb_channel_read(chan, &data);
- fail_unless(check == expected,
- "sdb_channel_read(chan, '') = %i; expected: %i",
- check, expected);
- if (check) {
- fail_unless(data == NULL,
- "sdb_channel_read() modified data to '%s'; "
- "expected: no modification", data);
- }
- else {
- fail_unless(data != NULL,
- "sdb_channel_read() did not return any data");
- fail_unless(!strcmp(data, golden_data_string[i].data),
- "sdb_channel_read() returned data '%s'; expected: '%s'",
- data, golden_data_string[i].data);
- }
- }
-}
-END_TEST
-
-START_TEST(test_write_read_string)
-{
- size_t i;
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data_string); ++i) {
- char *data = golden_data_string[i].data;
- int check = sdb_channel_write(chan, &data);
- fail_unless(check == 0,
- "sdb_channel_write(chan, '%s') = %i; expected: 0",
- data, check);
-
- data = NULL;
- check = sdb_channel_read(chan, &data);
- fail_unless(check == 0,
- "sdb_channel_read(chan, '') = %i; expected: 0", check);
- if (check) {
- fail_unless(data == NULL,
- "sdb_channel_read() modified data to '%s'; "
- "expected: no modifications", data);
- }
- else {
- fail_unless(data != NULL,
- "sdb_channel_read() did not return any data");
- fail_unless(!strcmp(data, golden_data_string[i].data),
- "sdb_channel_read() returned data '%s'; expected: '%s'",
- data, golden_data_string[i].data);
- }
- }
-}
-END_TEST
-
-Suite *
-util_channel_suite(void)
-{
- Suite *s = suite_create("utils::channel");
- TCase *tc;
-
- tc = tcase_create("core");
- tcase_add_test(tc, test_create);
- tcase_add_test(tc, test_write_read);
- tcase_add_test(tc, test_select);
- suite_add_tcase(s, tc);
-
- tc = tcase_create("integer");
- tcase_add_checked_fixture(tc, setup_int, teardown);
- tcase_add_test(tc, test_write_int);
- tcase_add_test(tc, test_read_int);
- tcase_add_test(tc, test_write_read_int);
- suite_add_tcase(s, tc);
-
- tc = tcase_create("string");
- tcase_add_checked_fixture(tc, setup_string, teardown);
- tcase_add_test(tc, test_write_string);
- tcase_add_test(tc, test_read_string);
- tcase_add_test(tc, test_write_read_string);
- suite_add_tcase(s, tc);
-
- return s;
-} /* util_llist_suite */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-
diff --git a/t/utils/dbi_test.c b/t/utils/dbi_test.c
--- a/t/utils/dbi_test.c
+++ /dev/null
@@ -1,645 +0,0 @@
-/*
- * SysDB - t/utils/dbi_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.
- */
-
-#if HAVE_CONFIG_H
-# include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include "libsysdb_test.h"
-#include "utils/dbi.h"
-
-#include <check.h>
-#include <dbi/dbi.h>
-
-#define TEST_MAGIC ((void *)0x1337)
-
-/*
- * private data-types
- */
-typedef union {
- long long integer;
- double decimal;
- const char *string;
- time_t datetime;
- struct {
- size_t length;
- const unsigned char *datum;
- } binary;
-} mock_data_t;
-
-typedef struct {
- const char *name;
- unsigned long long nrows;
- unsigned long long current_row;
- unsigned int nfields;
- unsigned short *field_types;
- char **field_names;
-} mock_query_t;
-
-/*
- * private variables
- */
-
-static sdb_dbi_client_t *client;
-
-/*
- * mock queries
- */
-
-/* field definitions */
-static unsigned short field_types[] = {
- DBI_TYPE_INTEGER,
- DBI_TYPE_DECIMAL,
- DBI_TYPE_STRING,
- DBI_TYPE_DATETIME,
- DBI_TYPE_BINARY,
-};
-static char *field_names[] = {
- "field0",
- "field1",
- "field2",
- "field3",
- "field4",
-};
-
-#define DATUM(p) ((const unsigned char *)(p))
-static mock_data_t golden_data[][5] = {
- {
- { .integer = 1234 },
- { .decimal = 1.234 },
- { .string = "abcd" },
- { .datetime = 0 },
- { .binary = { 1, DATUM("a") } },
- },
- {
- { .integer = 2345 },
- { .decimal = 23.45 },
- { .string = "bcde" },
- { .datetime = 1 },
- { .binary = { 4, DATUM("bcde") } },
- },
- {
- { .integer = 3456 },
- { .decimal = 345.6 },
- { .string = "cd" },
- { .datetime = 2 },
- { .binary = { 0, DATUM(NULL) } },
- },
- {
- { .integer = 4567 },
- { .decimal = 4567 },
- { .string = "d" },
- { .datetime = 3 },
- { .binary = { 13, DATUM("defghijklmnop") } },
- },
- {
- { .integer = 5678 },
- { .decimal = 56.78 },
- { .string = "efgh" },
- { .datetime = 4 },
- { .binary = { 5, DATUM("efghi") } },
- },
-};
-
-/* query definitions */
-static mock_query_t mock_queries[] = {
- { "mockquery0", 5, 1, 0, NULL, NULL },
- { "mockquery1", 0, 0, 1, field_types, field_names },
- { "mockquery2", 1, 1, 1, field_types, field_names },
- { "mockquery3", 2, 1, 1, field_types, field_names },
- { "mockquery4", 5, 1, 1, field_types, field_names },
- { "mockquery5", 5, 1, 2, field_types, field_names },
- { "mockquery6", 5, 1, 3, field_types, field_names },
- { "mockquery7", 5, 1, 4, field_types, field_names },
- { "mockquery8", 5, 1, 5, field_types, field_names },
-};
-
-static mock_query_t *current_query = NULL;
-
-/*
- * mocked functions
- */
-
-/* dbi_driver, dbi_conn, dbi_result are void pointers */
-
-dbi_driver
-dbi_driver_open(const char *name)
-{
- if (strcmp(name, "mockdriver"))
- return NULL;
- return (dbi_driver)strdup(name);
-} /* dbi_driver_open */
-
-dbi_driver
-dbi_driver_list(dbi_driver curr)
-{
- if (!curr)
- return "mockdriver";
- return NULL;
-} /* dbi_driver_list */
-
-const char *
-dbi_driver_get_name(dbi_driver driver)
-{
- return (const char *)driver;
-} /* dbi_driver_get_name */
-
-int
-dbi_conn_set_option(dbi_conn __attribute__((unused)) conn,
- const char __attribute__((unused)) *key,
- const char __attribute__((unused)) *value)
-{
- return 0;
-} /* dbi_conn_set_option */
-
-const char *
-dbi_conn_get_option_list(dbi_conn __attribute__((unused)) conn,
- const char __attribute__((unused)) *key)
-{
- return NULL;
-} /* dbi_conn_get_option_list */
-
-dbi_conn
-dbi_conn_open(dbi_driver driver)
-{
- if (strcmp((const char *)driver, "mockdriver"))
- return NULL;
- return (dbi_conn)"mockconnection";
-} /* dbi_conn_open */
-
-static unsigned long long dbi_conn_connect_called = 0;
-int
-dbi_conn_connect(dbi_conn conn)
-{
- ++dbi_conn_connect_called;
- if (strcmp((const char *)conn, "mockconnection"))
- return DBI_ERROR_NOCONN;
- return 0;
-} /* dbi_conn_connect */
-
-int
-dbi_conn_ping(dbi_conn conn)
-{
- if (strcmp((const char *)conn, "mockconnection"))
- return 0;
- return 1;
-} /* dbi_conn_connect */
-
-void
-dbi_conn_close(dbi_conn __attribute__((unused)) conn)
-{
- return;
-} /* dbi_conn_close */
-
-int
-dbi_conn_error(dbi_conn conn, const char **errmsg)
-{
- if ((! conn) || (strcmp((const char *)conn, "mockconnection")))
- return DBI_ERROR_BADOBJECT;
- if (errmsg)
- *errmsg = "mockerror";
- return DBI_ERROR_UNSUPPORTED;
-} /* dbi_conn_error */
-
-static unsigned long long dbi_conn_query_called = 0;
-dbi_result
-dbi_conn_query(dbi_conn conn, const char __attribute__((unused)) *statement)
-{
- size_t i;
-
- ++dbi_conn_query_called;
- if (strcmp((const char *)conn, "mockconnection"))
- return NULL;
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(mock_queries); ++i) {
- if (!strcmp(mock_queries[i].name, statement)) {
- current_query = &mock_queries[i];
- return (dbi_result)current_query;
- }
- }
- return NULL;
-} /* dbi_conn_query */
-
-unsigned long long
-dbi_result_get_numrows(dbi_result res)
-{
- mock_query_t *q = res;
- if (! q)
- return DBI_ROW_ERROR;
- return q->nrows;
-} /* dbi_result_get_numrows */
-
-unsigned int
-dbi_result_get_numfields(dbi_result res)
-{
- mock_query_t *q = res;
- if (! q)
- return DBI_FIELD_ERROR;
- return q->nfields;
-} /* dbi_result_get_numfields */
-
-unsigned short
-dbi_result_get_field_type_idx(dbi_result res, unsigned int i)
-{
- mock_query_t *q = res;
- if ((! q) || (i > q->nfields))
- return DBI_TYPE_ERROR;
- return q->field_types[i - 1];
-} /* dbi_result_get_field_type_idx */
-
-const char *
-dbi_result_get_field_name(dbi_result res, unsigned int i)
-{
- mock_query_t *q = res;
- if ((! q) || (i > q->nfields))
- return NULL;
- return q->field_names[i - 1];
-} /* dbi_result_get_field_name */
-
-int
-dbi_result_seek_row(dbi_result res, unsigned long long n)
-{
- mock_query_t *q = res;
- if ((! q) || (n > q->nrows))
- return 0;
-
- q->current_row = n;
- return 1;
-} /* dbi_result_seek_row */
-
-static mock_data_t
-get_golden_data(dbi_result res, unsigned int i) {
- mock_query_t *q = res;
- fail_unless(q != NULL, "dbi_result_get_*_idx() called with "
- "NULL result data; expected valid result object");
-
- /* this information is managed by seek_row and, thus,
- * should never be invalid */
- fail_unless(q->current_row && q->current_row <= q->nrows,
- "INTERNAL ERROR: current row out of range");
-
- fail_unless(i && i <= q->nfields,
- "dbi_result_get_*_idx() called with index out of range; "
- "got: %u; expected [1, %u]", i, q->nfields);
- return golden_data[q->current_row - 1][i - 1];
-} /* get_golden_data */
-
-long long
-dbi_result_get_longlong_idx(dbi_result res, unsigned int i)
-{
- fail_unless(current_query->field_types[i - 1] == DBI_TYPE_INTEGER,
- "dbi_result_get_longlong_idx() called for non-integer "
- "column type %u", current_query->field_types[i - 1]);
- return get_golden_data(res, i).integer;
-} /* dbi_result_get_longlong_idx */
-
-double
-dbi_result_get_double_idx(dbi_result res, unsigned int i)
-{
- fail_unless(current_query->field_types[i - 1] == DBI_TYPE_DECIMAL,
- "dbi_result_get_double_idx() called for non-decimal "
- "column type %u", current_query->field_types[i - 1]);
- return get_golden_data(res, i).decimal;
-} /* dbi_result_get_double_idx */
-
-const char *
-dbi_result_get_string_idx(dbi_result res, unsigned int i)
-{
- fail_unless(current_query->field_types[i - 1] == DBI_TYPE_STRING,
- "dbi_result_get_string_idx() called for non-string "
- "column type %u", current_query->field_types[i - 1]);
- return get_golden_data(res, i).string;
-} /* dbi_result_get_string_idx */
-
-char *
-dbi_result_get_string_copy_idx(dbi_result res, unsigned int i)
-{
- fail_unless(current_query->field_types[i - 1] == DBI_TYPE_STRING,
- "dbi_result_get_string_copy_idx() called for non-string "
- "column type %u", current_query->field_types[i - 1]);
- if (! get_golden_data(res, i).string)
- return NULL;
- return strdup(get_golden_data(res, i).string);
-} /* dbi_result_get_string_copy_idx */
-
-time_t
-dbi_result_get_datetime_idx(dbi_result res, unsigned int i)
-{
- fail_unless(current_query->field_types[i - 1] == DBI_TYPE_DATETIME,
- "dbi_result_get_datetime_idx() called for non-datetime "
- "column type %u", current_query->field_types[i - 1]);
- return get_golden_data(res, i).datetime;
-} /* dbi_result_get_datetime_idx */
-
-size_t
-dbi_result_get_field_length_idx(dbi_result res, unsigned int i)
-{
- /* this will check if the parameters are valid */
- get_golden_data(res, i);
-
- switch (current_query->field_types[i - 1]) {
- case DBI_TYPE_INTEGER:
- return sizeof(long long);
- break;
- case DBI_TYPE_DECIMAL:
- return sizeof(double);
- break;
- case DBI_TYPE_STRING:
- return strlen(get_golden_data(res, i).string) + 1;
- break;
- case DBI_TYPE_DATETIME:
- return sizeof(time_t);
- break;
- case DBI_TYPE_BINARY:
- return get_golden_data(res, i).binary.length;
- break;
- }
-
- fail("INTERNAL ERROR: dbi_result_get_field_length_idx() "
- "called for unexpected field type %u",
- current_query->field_types[i - 1]);
- return 0;
-} /* dbi_result_get_field_length_idx */
-
-const unsigned char *
-dbi_result_get_binary_idx(dbi_result res, unsigned int i)
-{
- fail_unless(current_query->field_types[i - 1] == DBI_TYPE_BINARY,
- "dbi_result_get_binary_idx() called for non-binary "
- "column type %u", current_query->field_types[i - 1]);
- return get_golden_data(res, i).binary.datum;
-} /* dbi_result_get_binary_idx */
-
-unsigned char *
-dbi_result_get_binary_copy_idx(dbi_result res, unsigned int i)
-{
- const char *data;
- fail_unless(current_query->field_types[i - 1] == DBI_TYPE_BINARY,
- "dbi_result_get_binary_copy_idx() called for non-binary "
- "column type %u", current_query->field_types[i - 1]);
- data = (const char *)get_golden_data(res, i).binary.datum;
- if (! data)
- return NULL;
- return (unsigned char *)strdup(data);
-} /* dbi_result_get_binary_copy_idx */
-
-static unsigned long long dbi_result_free_called = 0;
-int
-dbi_result_free(dbi_result res)
-{
- mock_query_t *q = res;
-
- ++dbi_result_free_called;
- if (! q)
- return -1;
-
- current_query = NULL;
- return 0;
-} /* dbi_result_free */
-
-/*
- * private helper functions
- */
-
-static void
-setup(void)
-{
- client = sdb_dbi_client_create("mockdriver", "mockdatabase");
- fail_unless(client != NULL,
- "sdb_dbi_client_create() = NULL; expected client object");
-
- dbi_conn_connect_called = 0;
-} /* setup */
-
-static void
-connect(void)
-{
- int check = sdb_dbi_client_connect(client);
- fail_unless(check == 0,
- "sdb_dbi_client_connect() = %i; expected: 0", check);
-} /* connect */
-
-static void
-teardown(void)
-{
- sdb_dbi_client_destroy(client);
- client = NULL;
-} /* teardown */
-
-static unsigned long long query_callback_called = 0;
-static int
-query_callback(sdb_dbi_client_t *c,
- size_t n, sdb_data_t *data, sdb_object_t *user_data)
-{
- size_t i;
-
- ++query_callback_called;
-
- fail_unless(c == client,
- "query callback received unexpected client = %p; "
- "expected: %p", c, client);
- fail_unless(n == current_query->nfields,
- "query callback received n = %i; expected: %i",
- n, current_query->nfields);
- fail_unless(data != NULL,
- "query callback received data = NULL; expected: valid data");
- fail_unless(user_data == TEST_MAGIC,
- "query callback received user_data = %p; expected: %p",
- user_data, TEST_MAGIC);
-
- for (i = 0; i < n; ++i) {
- int expected_type = DBI_TYPE_TO_SC(current_query->field_types[i]);
- mock_data_t expected_data;
-
- fail_unless((int)data[i].type == expected_type,
- "query callback received unexpected type %i for "
- "column %zu; expected: %i", data[i].type, i,
- expected_type);
-
- expected_data = golden_data[current_query->current_row - 1][i];
- switch (expected_type) {
- case SDB_TYPE_INTEGER:
- fail_unless(data[i].data.integer == expected_data.integer,
- "query callback received unexpected data %lli "
- "for column %zu; expected: %lli",
- data[i].data.integer, i, expected_data.integer);
- break;
- case SDB_TYPE_DECIMAL:
- fail_unless(data[i].data.decimal == expected_data.decimal,
- "query callback received unexpected data %g "
- "for column %zu; expected: %g",
- data[i].data.decimal, i, expected_data.decimal);
- break;
- case SDB_TYPE_STRING:
- fail_unless(!strcmp(data[i].data.string, expected_data.string),
- "query callback received unexpected data %s "
- "for column %zu; expected: %s",
- data[i].data.string, i, expected_data.string);
- break;
- case SDB_TYPE_DATETIME:
- fail_unless(data[i].data.datetime
- == SECS_TO_SDB_TIME(expected_data.datetime),
- "query callback received unexpected data "PRIscTIME
- " for column %zu; expected: "PRIscTIME,
- data[i].data.integer, i,
- SECS_TO_SDB_TIME(expected_data.integer));
- break;
- case SDB_TYPE_BINARY:
- fail_unless(data[i].data.binary.length ==
- expected_data.binary.length,
- "query callback received unexpected "
- "binary data length %zu for column %zu; "
- "expected: %lli", data[i].data.binary.length, i,
- expected_data.binary.length);
- fail_unless(!memcmp(data[i].data.binary.datum,
- expected_data.binary.datum,
- expected_data.binary.length),
- "query callback received unexpected binary data %p "
- "for column %zu; expected: %p",
- data[i].data.binary.datum, i,
- expected_data.binary.datum);
- break;
- default:
- fail("INTERNAL ERROR: query callback received "
- "unknown type %i for column %zu",
- expected_type, i);
- }
- }
- return 0;
-} /* query_callback */
-
-/*
- * tests
- */
-
-START_TEST(test_sdb_dbi_client_connect)
-{
- int check = sdb_dbi_client_connect(client);
- fail_unless(check == 0,
- "sdb_dbi_client_connect() = %i; expected: 0", check);
-
- fail_unless(dbi_conn_connect_called == 1,
- "sdb_dbi_client_create() called dbi_conn_connect %i times; "
- "expected: 1", dbi_conn_connect_called);
-}
-END_TEST
-
-START_TEST(test_sdb_dbi_client_check_conn)
-{
- int check = sdb_dbi_client_check_conn(client);
- fail_unless(check == 0,
- "sdb_dbi_client_check_conn() = %i; expected: 0", check);
-
- /* the first call will actually connect to the database */
- fail_unless(dbi_conn_connect_called == 1,
- "sdb_dbi_client_check_conn() called dbi_conn_connect %i times; "
- "expected: 1", dbi_conn_connect_called);
-
- dbi_conn_connect_called = 0;
- check = sdb_dbi_client_check_conn(client);
- fail_unless(check == 0,
- "sdb_dbi_client_check_conn() = %i; expected: 0", check);
-
- /* should not reconnect */
- fail_unless(dbi_conn_connect_called == 0,
- "sdb_dbi_client_check_conn() called dbi_conn_connect %i time(s); "
- "expected: 0", dbi_conn_connect_called);
-}
-END_TEST
-
-START_TEST(test_sdb_dbi_exec_query)
-{
- size_t i;
-
- int check = sdb_dbi_exec_query(client, "mockquery0", query_callback,
- /* user_data = */ TEST_MAGIC, /* n = */ 0);
- /* not connected yet */
- fail_unless(check < 0,
- "sdb_dbi_exec_query() = %i; expected: < 0", check);
-
- connect();
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(mock_queries); ++i) {
- mock_query_t *q = &mock_queries[i];
-
- unsigned long long expected_callback_calls = 0;
-
- dbi_conn_query_called = 0;
- query_callback_called = 0;
- dbi_result_free_called = 0;
-
- /* sdb_dbi_exec_query will only use as many type arguments are needed,
- * so we can safely pass in the maximum number of arguments required
- * on each call */
- check = sdb_dbi_exec_query(client, q->name, query_callback,
- /* user_data = */ TEST_MAGIC, /* n = */ (int)q->nfields,
- SDB_TYPE_INTEGER, SDB_TYPE_DECIMAL, SDB_TYPE_STRING,
- SDB_TYPE_DATETIME, SDB_TYPE_BINARY);
- fail_unless(check == 0,
- "sdb_dbi_exec_query() = %i; expected: 0", check);
-
- fail_unless(dbi_conn_query_called == 1,
- "sdb_dbi_exec_query() called dbi_conn_query %i times; "
- "expected: 1", dbi_conn_query_called);
-
- if (q->nfields)
- expected_callback_calls = q->nrows;
-
- fail_unless(query_callback_called == expected_callback_calls,
- "sdb_dbi_exec_query() did not call the registered callback "
- "for each result row; got %i call%s; expected: 0",
- query_callback_called,
- (query_callback_called == 1) ? "" : "s");
-
- fail_unless(dbi_result_free_called == 1,
- "sdb_dbi_exec_query() did not free the query result object");
- }
-}
-END_TEST
-
-/*
- * test API
- */
-
-Suite *
-util_dbi_suite(void)
-{
- Suite *s = suite_create("utils::dbi");
- TCase *tc;
-
- tc = tcase_create("core");
- tcase_add_checked_fixture(tc, setup, teardown);
- tcase_add_test(tc, test_sdb_dbi_client_connect);
- tcase_add_test(tc, test_sdb_dbi_client_check_conn);
- tcase_add_test(tc, test_sdb_dbi_exec_query);
- suite_add_tcase(s, tc);
-
- return s;
-} /* util_llist_suite */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-
diff --git a/t/utils/llist_test.c b/t/utils/llist_test.c
--- a/t/utils/llist_test.c
+++ /dev/null
@@ -1,399 +0,0 @@
-/*
- * SysDB - t/utils/llist_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 "utils/llist.h"
-#include "libsysdb_test.h"
-
-#include <check.h>
-
-/*
- * private data types
- */
-
-static sdb_object_t golden_data[] = {
- SSTRING_OBJ("abc"),
- SSTRING_OBJ("bcd"),
- SSTRING_OBJ("cde"),
- SSTRING_OBJ("def"),
- SSTRING_OBJ("efg"),
- SSTRING_OBJ("fgh"),
- SSTRING_OBJ("ghi")
-};
-
-static char *unused_names[] = {
- "xyz",
- "yza",
- "zab"
-};
-
-static sdb_llist_t *list;
-
-static void
-setup(void)
-{
- list = sdb_llist_create();
- fail_unless(list != NULL,
- "sdb_llist_create() = NULL; expected list object");
-} /* setup */
-
-static void
-teardown(void)
-{
- sdb_llist_destroy(list);
- list = NULL;
-} /* teardown */
-
-/* populate the list with the golden data in the specified order */
-static void
-populate(void)
-{
- size_t i;
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- int check = sdb_llist_append(list, &golden_data[i]);
- fail_unless(check == 0,
- "sdb_llist_append(%s) = %i; expected: 0",
- golden_data[i].name, check);
- }
-} /* populate */
-
-START_TEST(test_clone)
-{
- sdb_llist_t *clone;
- size_t i;
-
- populate();
-
- clone = sdb_llist_clone(list);
- fail_unless(clone != NULL,
- "sdb_llist_clone() = NULL; expected: cloned list object");
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- fail_unless(golden_data[i].ref_cnt == 3,
- "sdb_llist_clone() did not take ownership");
- }
-
- sdb_llist_destroy(clone);
-}
-END_TEST
-
-START_TEST(test_destroy)
-{
- size_t i;
- populate();
- sdb_llist_destroy(list);
- list = NULL;
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- fail_unless(golden_data[i].ref_cnt == 1,
- "sdb_llist_destroy() did not deref element %s",
- golden_data[i].name);
- }
-}
-END_TEST
-
-START_TEST(test_clear)
-{
- size_t i;
- populate();
- sdb_llist_clear(list);
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- fail_unless(golden_data[i].ref_cnt == 1,
- "sdb_llist_clear() did not deref element %s",
- golden_data[i].name);
- }
-
- i = sdb_llist_len(list);
- fail_unless(i == 0,
- "sdb_llist_clear() left %zu elements in the list; "
- "expected: 0", i);
-}
-END_TEST
-
-START_TEST(test_append)
-{
- size_t i;
-
- fail_unless(sdb_llist_len(list) == 0,
- "sdb_llist_len(<empty list>) = %zu; expected: 0",
- sdb_llist_len(list));
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- int check = sdb_llist_append(list, &golden_data[i]);
- fail_unless(check == 0,
- "sdb_llist_append(%s) = %i; expected: 0",
- golden_data[i].name, check);
- fail_unless(golden_data[i].ref_cnt == 2,
- "sdb_llist_append(%s) did not take ownership",
- golden_data[i].name);
- fail_unless(sdb_llist_len(list) == i + 1,
- "sdb_llist_len(<empty list>) = %zu; expected: zu",
- sdb_llist_len(list), i + 1);
- }
-}
-END_TEST
-
-START_TEST(test_insert)
-{
- size_t i;
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- int check = sdb_llist_insert(list, &golden_data[i], 0);
- fail_unless(check == 0,
- "sdb_llist_insert(%s, 0) = %i; expected: 0",
- golden_data[i].name, check);
- fail_unless(golden_data[i].ref_cnt == 2,
- "sdb_llist_insert(%s, 0) did not take ownership",
- golden_data[i].name);
- }
-}
-END_TEST
-
-START_TEST(test_validate_insert)
-{
- size_t i;
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- /* none of these operations will succeed
- * => 1 is invalid for each case */
- int check = sdb_llist_insert(list, &golden_data[i], 1);
- fail_unless(check == -1,
- "sdb_llist_insert(%s, 1) = %i; expected: -1",
- golden_data[i].name, check);
- fail_unless(golden_data[i].ref_cnt == 1,
- "sdb_llist_insert(%s, 1) took ownership",
- golden_data[i].name);
- }
-}
-END_TEST
-
-START_TEST(test_get)
-{
- size_t i;
- populate();
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- sdb_object_t *check = sdb_llist_get(list, i);
- fail_unless(check == &golden_data[i],
- "sdb_llist_get() = %p; expected: %p",
- check, &golden_data[i]);
- fail_unless(check->ref_cnt == 3,
- "sdb_llist_get() didn't increment reference count; got: %i; "
- "expected: 3", check->ref_cnt);
- sdb_object_deref(check);
- }
-}
-END_TEST
-
-START_TEST(test_remove_by_name)
-{
- /* "random" indexes */
- int indexes[] = { 4, 5, 3, 6, 2, 0, 1 };
- size_t i;
-
- populate();
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(indexes); ++i) {
- sdb_object_t *check;
-
- fail_unless((size_t)indexes[i] < SDB_STATIC_ARRAY_LEN(golden_data),
- "INTERNAL ERROR: invalid index %i", indexes[i]);
-
- check = sdb_llist_remove_by_name(list, golden_data[indexes[i]].name);
- fail_unless(check == &golden_data[indexes[i]],
- "sdb_llist_remove_by_name() = %p; expected: %p",
- check, &golden_data[indexes[i]]);
- fail_unless(check->ref_cnt == 2,
- "sdb_llist_remove_by_name() returned unexpected reference "
- "count; got: %i; expected: 2", check->ref_cnt);
-
- check = sdb_llist_remove_by_name(list, golden_data[indexes[i]].name);
- fail_unless(check == NULL,
- "sdb_llist_remove_by_name() did not remove the element");
- }
-}
-END_TEST
-
-static int
-dummy_lookup(const sdb_object_t __attribute__((unused)) *obj,
- const void __attribute__((unused)) *user_data)
-{
- return 0;
-} /* dummy_lookup */
-
-START_TEST(test_search)
-{
- size_t i;
- populate();
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- sdb_object_t *check = sdb_llist_search_by_name(list,
- golden_data[i].name);
- fail_unless(check == &golden_data[i],
- "sdb_llist_search_by_name(%s) = NULL; expected: %p",
- golden_data[i].name, &golden_data[i]);
- }
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(unused_names); ++i) {
- sdb_object_t *check = sdb_llist_search_by_name(list,
- unused_names[i]);
- fail_unless(check == NULL,
- "sdb_llist_search_by_name(%s) = %p; expected: NULL",
- unused_names[i], check);
- }
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- /* dummy_lookup always return 0, thus, this will always return the
- * first element */
- sdb_object_t *check = sdb_llist_search(list, dummy_lookup, NULL);
- fail_unless(check == &golden_data[i],
- "sdb_llist_search() = %p (%s); expected: %p (%s)",
- check, check->name, &golden_data[i], golden_data[i].name);
-
- /* => remove the first element */
- check = sdb_llist_remove(list, dummy_lookup, NULL);
- fail_unless(check == &golden_data[i],
- "sdb_llist_remove() = %p (%s); expected: %p (%s)",
- check, check->name, &golden_data[i], golden_data[i].name);
- fail_unless(check->ref_cnt == 2,
- "sdb_llist_remove() changed reference count; got: %i; "
- "expected: 2", check->ref_cnt);
- }
- /* should now be empty */
- fail_unless(sdb_llist_len(list) == 0,
- "Still have %i elements in the list; expected: 0",
- sdb_llist_len(list));
-}
-END_TEST
-
-START_TEST(test_shift)
-{
- size_t i;
- populate();
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- sdb_object_t *check = sdb_llist_shift(list);
- fail_unless(check == &golden_data[i],
- "sdb_llist_shift() = NULL; expected: %p",
- &golden_data[i]);
- fail_unless(check->ref_cnt == 2,
- "sdb_llist_shift() changed reference count; got: %i; "
- "expected: 2", check->ref_cnt);
- }
-
- /* must be empty now */
- fail_unless(sdb_llist_shift(list) == NULL,
- "sdb_llist_shift() returned value; expected: NULL");
-}
-END_TEST
-
-START_TEST(test_iter)
-{
- sdb_llist_iter_t *iter;
- size_t i;
-
- populate();
-
- iter = sdb_llist_get_iter(list);
- fail_unless(iter != NULL,
- "sdb_llist_get_iter() did not return an iterator");
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- sdb_object_t *check;
- fail_unless(sdb_llist_iter_has_next(iter),
- "sdb_llist_iter_has_next() = FALSE; expected: TRUE");
- check = sdb_llist_iter_get_next(iter);
- fail_unless(check == &golden_data[i],
- "sdb_llist_iter_get_next() = %p; expected: %p",
- check, &golden_data[i]);
- }
-
- fail_unless(!sdb_llist_iter_has_next(iter),
- "sdb_llist_iter_has_next() = TRUE; expected: FALSE");
- fail_unless(sdb_llist_iter_get_next(iter) == NULL,
- "sdb_llist_iter_get_next() returned value; expected: NULL");
- sdb_llist_iter_destroy(iter);
-}
-END_TEST
-
-START_TEST(test_iter_remove)
-{
- sdb_llist_iter_t *iter;
- sdb_object_t *check;
- size_t i;
-
- populate();
-
- iter = sdb_llist_get_iter(list);
- fail_unless(iter != NULL,
- "sdb_llist_get_iter() did not return an iterator");
-
- i = 0;
- while (sdb_llist_iter_has_next(iter)) {
- check = sdb_llist_iter_get_next(iter);
- fail_unless(check == &golden_data[i],
- "sdb_llist_iter_get_next() = %p; expected: %p",
- check, &golden_data[i]);
-
- sdb_llist_iter_remove_current(iter);
- ++i;
- }
- sdb_llist_iter_destroy(iter);
-
- fail_unless(i == (size_t)SDB_STATIC_ARRAY_LEN(golden_data),
- "iterated for %zu steps; expected: %i",
- i, SDB_STATIC_ARRAY_LEN(golden_data));
-
- /* all elements should be removed */
- check = sdb_llist_shift(list);
- fail_unless(check == NULL,
- "sdb_llist_shift() = %p; expected: NULL", check);
-}
-END_TEST
-
-Suite *
-util_llist_suite(void)
-{
- Suite *s = suite_create("utils::llist");
- TCase *tc;
-
- tc = tcase_create("core");
- tcase_add_checked_fixture(tc, setup, teardown);
- tcase_add_test(tc, test_clone);
- tcase_add_test(tc, test_destroy);
- tcase_add_test(tc, test_clear);
- tcase_add_test(tc, test_append);
- tcase_add_test(tc, test_insert);
- tcase_add_test(tc, test_validate_insert);
- tcase_add_test(tc, test_get);
- tcase_add_test(tc, test_remove_by_name);
- tcase_add_test(tc, test_search);
- tcase_add_test(tc, test_shift);
- tcase_add_test(tc, test_iter);
- tcase_add_test(tc, test_iter_remove);
- suite_add_tcase(s, tc);
-
- return s;
-} /* util_llist_suite */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-
diff --git a/t/utils/strbuf_test.c b/t/utils/strbuf_test.c
--- a/t/utils/strbuf_test.c
+++ /dev/null
@@ -1,564 +0,0 @@
-/*
- * SysDB - t/utils/strbuf_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 "utils/strbuf.h"
-#include "libsysdb_test.h"
-
-#include <check.h>
-
-/*
- * private data types
- */
-
-static sdb_strbuf_t *buf;
-
-static void
-setup(void)
-{
- buf = sdb_strbuf_create(0);
- fail_unless(buf != NULL,
- "sdb_strbuf_create() = NULL; expected strbuf object");
-} /* setup */
-
-static void
-teardown(void)
-{
- sdb_strbuf_destroy(buf);
- buf = NULL;
-} /* teardown */
-
-/*
- * tests
- */
-
-START_TEST(test_null)
-{
- sdb_strbuf_t *b = NULL;
- va_list ap;
-
- /* check that methods don't crash */
- sdb_strbuf_destroy(b);
- sdb_strbuf_skip(b, 0, 0);
- sdb_strbuf_skip(b, 0, 10);
- sdb_strbuf_skip(b, 10, 10);
- sdb_strbuf_clear(b);
-
- /* check that methods return an error */
- fail_unless(sdb_strbuf_vappend(b, "test", ap) < 0,
- "sdb_strbuf_vappend(NULL) didn't report failure");
- fail_unless(sdb_strbuf_append(b, "test") < 0,
- "sdb_strbuf_append(NULL) didn't report failure");
- fail_unless(sdb_strbuf_vsprintf(b, "test", ap) < 0,
- "sdb_strbuf_vsprintf(NULL) didn't report failure");
- fail_unless(sdb_strbuf_sprintf(b, "test") < 0,
- "sdb_strbuf_sprintf(NULL) didn't report failure");
- fail_unless(sdb_strbuf_memcpy(b, "test", 4) < 0,
- "sdb_strbuf_memcpy(NULL) didn't report failure");
- fail_unless(sdb_strbuf_memappend(b, "test", 4) < 0,
- "sdb_strbuf_memappend(NULL) didn't report failure");
- fail_unless(sdb_strbuf_read(b, 0, 32) < 0,
- "sdb_strbuf_read(NULL) didn't report failure");
- fail_unless(sdb_strbuf_chomp(b) < 0,
- "sdb_strbuf_chomp(NULL) didn't report failure");
-}
-END_TEST
-
-START_TEST(test_empty)
-{
- sdb_strbuf_t *b = sdb_strbuf_create(0);
- const char *data;
- size_t len;
-
- /* check that methods don't crash */
- sdb_strbuf_skip(b, 1, 1);
- sdb_strbuf_clear(b);
- sdb_strbuf_chomp(b);
-
- data = sdb_strbuf_string(b);
- fail_unless(data && (*data == '\0'),
- "sdb_strbuf_string(<empty>) = '%s'; expected: ''", data);
- len = sdb_strbuf_len(b);
- fail_unless(len == 0,
- "sdb_strbuf_len(<empty>) = %zu; expected: 0", len);
-
- sdb_strbuf_destroy(b);
-}
-END_TEST
-
-START_TEST(test_sdb_strbuf_create)
-{
- sdb_strbuf_t *s;
- size_t len;
-
- s = sdb_strbuf_create(0);
- fail_unless(s != NULL,
- "sdb_strbuf_create() = NULL; expected strbuf object");
- len = sdb_strbuf_len(s);
- fail_unless(len == 0,
- "sdb_strbuf_create() created buffer with len = %zu; "
- "expected: 0", len);
- sdb_strbuf_destroy(s);
-
- s = sdb_strbuf_create(128);
- fail_unless(s != NULL,
- "sdb_strbuf_create() = NULL; expected strbuf object");
- len = sdb_strbuf_len(s);
- /* len still has to be 0 -- there's no content */
- fail_unless(len == 0,
- "sdb_strbuf_create() created buffer with len = %zu; "
- "expected: 0", len);
- sdb_strbuf_destroy(s);
-}
-END_TEST
-
-START_TEST(test_sdb_strbuf_append)
-{
- ssize_t n, expected;
- size_t len;
- const char *test;
-
- n = sdb_strbuf_append(buf, "1234567890");
- fail_unless(n == 10,
- "sdb_strbuf_append() appended %zi bytes; expected: 10", n);
- len = sdb_strbuf_len(buf);
- fail_unless(len == 10,
- "sdb_strbuf_append() left behind buffer with len = %zu; "
- "expected: 10", len);
-
- n = sdb_strbuf_append(buf, "ABCDE");
- fail_unless(n == 5,
- "sdb_strbuf_append() appended %zi bytes; expected: 5", n);
- len = sdb_strbuf_len(buf);
- fail_unless(len == 15,
- "sdb_strbuf_append() left behind buffer with len = %zu; "
- "expected: 15", len);
-
- test = sdb_strbuf_string(buf);
- fail_unless(test[len] == '\0',
- "sdb_strbuf_append() did not nil-terminate the string");
- fail_unless(!strcmp(test, "1234567890ABCDE"),
- "sdb_strbuf_append() did not correctly concatenate two string; "
- "got: %s; expected: 1234567890ABCDE", test);
-
- n = sdb_strbuf_append(buf, "%zu; %5.4f", len, (double)len / 10.0);
- expected = /* len */ 2 + /* "; " */ 2 + /* decimal len/10 */ 6;
- fail_unless(n == expected,
- "sdb_strbuf_append() appended %zi bytes; expected: %zi",
- n, expected);
- len = sdb_strbuf_len(buf);
- fail_unless(len == 15 + (size_t)expected,
- "sdb_strbuf_append() left behind buffer with len = %zu; "
- "expected: %zu", len, 15 + (size_t)expected);
-
- test = sdb_strbuf_string(buf);
- fail_unless(test[len] == '\0',
- "sdb_strbuf_append() did not nil-terminate the string");
- fail_unless(!strcmp(test, "1234567890ABCDE15; 1.5000"),
- "sdb_strbuf_append() did not correctly concatenate two string; "
- "got: %s; expected: 1234567890ABCDE15; 1.5000", test);
-}
-END_TEST
-
-START_TEST(test_sdb_strbuf_sprintf)
-{
- ssize_t n, expected;
- size_t len;
- const char *test;
-
- n = sdb_strbuf_sprintf(buf, "1234567890");
- fail_unless(n == 10,
- "sdb_strbuf_sprintf() wrote %zi bytes; expected: 10", n);
- len = sdb_strbuf_len(buf);
- fail_unless(len == 10,
- "sdb_strbuf_sprintf() left behind buffer with len = %zu; "
- "expected: 10", len);
-
- n = sdb_strbuf_sprintf(buf, "ABCDE");
- fail_unless(n == 5,
- "sdb_strbuf_sprintf() wrote %zi bytes; expected: 5", n);
- len = sdb_strbuf_len(buf);
- fail_unless(len == 5,
- "sdb_strbuf_sprintf() left behind buffer with len = %zu; "
- "expected: 5", len);
-
- test = sdb_strbuf_string(buf);
- fail_unless(test[len] == '\0',
- "sdb_strbuf_sprintf() did not nil-terminate the string");
- fail_unless(!strcmp(test, "ABCDE"),
- "sdb_strbuf_sprintf() did not format string correctly; "
- "got: %s; expected: ABCDE", test);
-
- n = sdb_strbuf_sprintf(buf, "%zu; %5.4f", len, (double)len / 10.0);
- expected = /* len */ 1 + /* "; " */ 2 + /* decimal len/10 */ 6;
- fail_unless(n == expected,
- "sdb_strbuf_sprintf() wrote %zi bytes; expected: %zi",
- n, expected);
- len = sdb_strbuf_len(buf);
- fail_unless(len == (size_t)expected,
- "sdb_strbuf_sprintf() left behind buffer with len = %zu; "
- "expected: %zu", len, (size_t)expected);
-
- test = sdb_strbuf_string(buf);
- fail_unless(test[len] == '\0',
- "sdb_strbuf_sprintf() did not nil-terminate the string");
- fail_unless(!strcmp(test, "5; 0.5000"),
- "sdb_strbuf_sprintf() did not format string correctly; "
- "got: %s; expected: 5; 0.5000", test);
-}
-END_TEST
-
-START_TEST(test_incremental)
-{
- ssize_t n;
- size_t i;
-
- sdb_strbuf_destroy(buf);
- buf = sdb_strbuf_create(1024);
-
- /* fill buffer one by one; leave room for nul-byte */
- for (i = 0; i < 1023; ++i) {
- n = sdb_strbuf_append(buf, ".");
- fail_unless(n == 1, "sdb_strbuf_append() = %zi; expected: 1", n);
- }
-
- /* write another byte; this has to trigger a resize */
- n = sdb_strbuf_append(buf, ".");
- fail_unless(n == 1, "sdb_strbuf_append() = %zi; expected: 1", n);
-
- /* write more bytes; this should trigger at least one more resize but
- * that's an implementation detail */
- for (i = 0; i < 1024; ++i) {
- n = sdb_strbuf_append(buf, ".");
- fail_unless(n == 1, "sdb_strbuf_append() = %zi; expected: 1", n);
- }
-
- n = (ssize_t)sdb_strbuf_len(buf);
- fail_unless(n == 2048, "sdb_strbuf_len() = %zi; expectd: 2048", n);
-}
-END_TEST
-
-static struct {
- const char *input;
- size_t size;
-} mem_golden_data[] = {
- { "abc\0\x10\x42", 6 },
- { "\0\1\2\3\4", 5 },
- { "\n\n\0\n\n", 5 },
- { "", 0 },
-};
-
-START_TEST(test_sdb_strbuf_memcpy)
-{
- size_t i;
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(mem_golden_data); ++i) {
- ssize_t n;
- const char *check;
-
- n = sdb_strbuf_memcpy(buf, mem_golden_data[i].input,
- mem_golden_data[i].size);
- fail_unless(n >= 0,
- "sdb_strbuf_memcpy() = %zi; expected: >=0", n);
- fail_unless((size_t)n == mem_golden_data[i].size,
- "sdb_strbuf_memcpy() = %zi; expected: %zu",
- n, mem_golden_data[i].size);
-
- n = (ssize_t)sdb_strbuf_len(buf);
- fail_unless((size_t)n == mem_golden_data[i].size,
- "sdb_strbuf_len() = %zu (after memcpy); expected: %zu",
- n, mem_golden_data[i].size);
-
- check = sdb_strbuf_string(buf);
- fail_unless(check != NULL,
- "sdb_strbuf_string() = NULL (after memcpy); expected: data");
- fail_unless(check[mem_golden_data[i].size] == '\0',
- "sdb_strbuf_memcpy() did not nil-terminate the data");
- fail_unless(!memcmp(check, mem_golden_data[i].input,
- mem_golden_data[i].size),
- "sdb_strbuf_memcpy() did not set the buffer correctly");
- }
-}
-END_TEST
-
-START_TEST(test_sdb_strbuf_memappend)
-{
- size_t i;
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(mem_golden_data); ++i) {
- ssize_t n;
- const char *check;
-
- size_t total, j;
-
- n = sdb_strbuf_memappend(buf, mem_golden_data[i].input,
- mem_golden_data[i].size);
- fail_unless(n >= 0,
- "sdb_strbuf_memappend() = %zi; expected: >=0", n);
- fail_unless((size_t)n == mem_golden_data[i].size,
- "sdb_strbuf_memappend() = %zi; expected: %zu",
- n, mem_golden_data[i].size);
-
- check = sdb_strbuf_string(buf);
- fail_unless(check != NULL,
- "sdb_strbuf_string() = NULL (after memappend); "
- "expected: data");
-
- n = (ssize_t)sdb_strbuf_len(buf);
- total = 0;
- for (j = 0; j <= i; ++j) {
- fail_unless(total + mem_golden_data[j].size <= (size_t)n,
- "sdb_strbuf_len() = %zu (after memappend); "
- "expected: >=%zu", n, total + mem_golden_data[j].size);
-
- fail_unless(!memcmp(check + total, mem_golden_data[j].input,
- mem_golden_data[j].size),
- "sdb_strbuf_memappend() did not "
- "set the buffer correctly");
- total += mem_golden_data[j].size;
- }
- fail_unless((size_t)n == total,
- "sdb_strbuf_len() = %zu (after memappend); expected: %zu",
- n, total);
-
- fail_unless(check[total] == '\0',
- "sdb_strbuf_memappend() did not nil-terminate the data");
- }
-}
-END_TEST
-
-static struct {
- const char *input;
- ssize_t expected;
- const char *expected_string;
-} chomp_golden_data[] = {
- { NULL, 0, "" },
- { "\n", 1, "" },
- { "\n\n", 2, "" },
- { "12345\n\n\n", 3, "12345" },
- { "abcd", 0, "abcd" },
-};
-
-START_TEST(test_sdb_strbuf_chomp)
-{
- size_t i;
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(chomp_golden_data); ++i) {
- ssize_t n;
- const char *check;
-
- if (chomp_golden_data[i].input)
- sdb_strbuf_sprintf(buf, chomp_golden_data[i].input);
-
- /* empty buffer */
- n = sdb_strbuf_chomp(buf);
- fail_unless(n == chomp_golden_data[i].expected,
- "sdb_strbuf_chomp() = %zi; expected: %zi", n,
- chomp_golden_data[i].expected);
-
- check = sdb_strbuf_string(buf);
- fail_unless(!strcmp(check, chomp_golden_data[i].expected_string),
- "sdb_strbuf_chomp() did not correctly remove newlines; "
- "got string '%s'; expected: '%s'", check,
- chomp_golden_data[i].expected_string);
- }
-}
-END_TEST
-
-/* input is "1234567890" */
-static struct {
- size_t offset;
- size_t n;
- const char *expected;
- size_t expected_len;
-} skip_golden_data[] = {
- { 0, 0, "1234567890", 10 },
- { 0, 1, "234567890", 9 },
- { 0, 2, "34567890", 8 },
- { 0, 9, "0", 1 },
- { 0, 10, "", 0 },
- { 0, 11, "", 0 },
- { 0, 100, "", 0 },
- { 1, 0, "1234567890", 10 },
- { 1, 1, "134567890", 9 },
- { 1, 2, "14567890", 8 },
- { 2, 0, "1234567890", 10 },
- { 2, 1, "124567890", 9 },
- { 2, 2, "12567890", 8 },
- { 2, 3, "1267890", 7 },
- { 2, 4, "127890", 6 },
- { 2, 5, "12890", 5 },
- { 2, 6, "1290", 4 },
- { 2, 7, "120", 3 },
- { 2, 8, "12", 2 },
- { 2, 9, "12", 2 },
- { 2, 10, "12", 2 },
- { 8, 1, "123456780", 9 },
- { 8, 2, "12345678", 8 },
- { 8, 3, "12345678", 8 },
- { 9, 1, "123456789", 9 },
- { 9, 2, "123456789", 9 },
- { 10, 1, "1234567890", 10 },
- { 10, 2, "1234567890", 10 },
-};
-
-START_TEST(test_sdb_strbuf_skip)
-{
- const char *input = "1234567890";
- size_t i;
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(skip_golden_data); ++i) {
- const char *check;
- size_t n;
-
- sdb_strbuf_sprintf(buf, input);
- sdb_strbuf_skip(buf, skip_golden_data[i].offset,
- skip_golden_data[i].n);
-
- n = sdb_strbuf_len(buf);
- fail_unless(n == skip_golden_data[i].expected_len,
- "sdb_strbuf_len() = %zu (after skip); expected: %zu",
- n, skip_golden_data[i].expected_len);
-
- check = sdb_strbuf_string(buf);
- fail_unless(!!check,
- "sdb_strbuf_string() = NULL (after skip); expected: string");
-
- fail_unless(check[n] == '\0',
- "sdb_strbuf_skip() did not nil-terminate the string");
-
- fail_unless(! strcmp(skip_golden_data[i].expected, check),
- "sdb_strbuf_skip('%s', %zu) did not skip correctly; "
- "got string '%s'; expected: '%s'", input,
- skip_golden_data[i].n, check, skip_golden_data[i].expected);
- }
-}
-END_TEST
-
-START_TEST(test_sdb_strbuf_clear)
-{
- const char *data;
- size_t len;
-
- sdb_strbuf_append(buf, "abc");
- len = sdb_strbuf_len(buf);
- fail_unless(len != 0,
- "sdb_strbuf_len() = %zu; expected: != 0", len);
-
- sdb_strbuf_clear(buf);
- len = sdb_strbuf_len(buf);
- fail_unless(len == 0,
- "sdb_strbuf_len() = %zu (after clear); expected: 0", len);
-
- data = sdb_strbuf_string(buf);
- fail_unless(*data == '\0',
- "sdb_strbuf_string() = '%s' (after clear); expected: ''", data);
-}
-END_TEST
-
-static struct {
- const char *input;
- const char *expected;
-} string_golden_data[] = {
- { NULL, "" },
- { "a", "a" },
- { "abcdef", "abcdef" },
-};
-
-START_TEST(test_sdb_strbuf_string)
-{
- size_t i;
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(string_golden_data); ++i) {
- const char *check;
-
- if (string_golden_data[i].input)
- sdb_strbuf_sprintf(buf, string_golden_data[i].input);
- check = sdb_strbuf_string(buf);
- fail_unless(!strcmp(check, string_golden_data[i].expected),
- "sdb_strbuf_string() = '%s'; expected: '%s'",
- check, string_golden_data[i].expected);
- }
-}
-END_TEST
-
-static struct {
- const char *input;
- size_t expected;
-} len_golden_data[] = {
- { NULL, 0 },
- { "a", 1 },
- { "12345", 5 },
-};
-
-START_TEST(test_sdb_strbuf_len)
-{
- size_t i;
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(len_golden_data); ++i) {
- size_t check;
-
- if (len_golden_data[i].input)
- sdb_strbuf_sprintf(buf, len_golden_data[i].input);
- check = sdb_strbuf_len(buf);
- fail_unless(check == len_golden_data[i].expected,
- "sdb_strbuf_len() = %zu; expected: %zu",
- check, len_golden_data[i].expected);
- }
-}
-END_TEST
-
-Suite *
-util_strbuf_suite(void)
-{
- Suite *s = suite_create("utils::strbuf");
- TCase *tc;
-
- tc = tcase_create("empty");
- tcase_add_test(tc, test_null);
- tcase_add_test(tc, test_empty);
- suite_add_tcase(s, tc);
-
- tc = tcase_create("core");
- tcase_add_checked_fixture(tc, setup, teardown);
- tcase_add_test(tc, test_sdb_strbuf_create);
- tcase_add_test(tc, test_sdb_strbuf_append);
- tcase_add_test(tc, test_sdb_strbuf_sprintf);
- tcase_add_test(tc, test_incremental);
- tcase_add_test(tc, test_sdb_strbuf_memcpy);
- tcase_add_test(tc, test_sdb_strbuf_memappend);
- tcase_add_test(tc, test_sdb_strbuf_chomp);
- tcase_add_test(tc, test_sdb_strbuf_skip);
- tcase_add_test(tc, test_sdb_strbuf_clear);
- tcase_add_test(tc, test_sdb_strbuf_string);
- tcase_add_test(tc, test_sdb_strbuf_len);
- suite_add_tcase(s, tc);
-
- return s;
-} /* util_strbuf_suite */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-
diff --git a/t/utils/unixsock_test.c b/t/utils/unixsock_test.c
--- a/t/utils/unixsock_test.c
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- * SysDB - t/utils/unixsock_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.
- */
-
-#if HAVE_CONFIG_H
-# include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-/* required for fopencookie support */
-#define _GNU_SOURCE
-
-#include "utils/unixsock.h"
-#include "libsysdb_test.h"
-
-#include <check.h>
-
-#include <dlfcn.h>
-
-#include <errno.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <unistd.h>
-
-/*
- * I/O "hook" functions
- */
-
-typedef struct {
- int fd;
- size_t pos;
-} io_cookie_t;
-
-static struct {
- const char *data;
- size_t len;
-} golden_data[] = {
- { "a", 1 },
- { "abc", 3 },
- { "12345", 5 },
- { "", 0 },
-};
-
-static char *last_write = NULL;
-
-static unsigned long long mock_read_called = 0;
-static ssize_t
-mock_read(void *cookie, char *buf, size_t size)
-{
- io_cookie_t *c = cookie;
- ssize_t ret;
-
- ++mock_read_called;
-
- if (c->pos >= SDB_STATIC_ARRAY_LEN(golden_data))
- return 0;
-
- ret = snprintf(buf, size, "%s\n", golden_data[c->pos].data);
- ++c->pos;
- return ret;
-} /* mock_read */
-
-static unsigned long long mock_write_called = 0;
-static ssize_t
-mock_write(void *cookie, const char *buf, size_t size)
-{
- io_cookie_t *c = cookie;
-
- ++mock_write_called;
-
- if (c->pos >= SDB_STATIC_ARRAY_LEN(golden_data))
- return 0;
-
- if (last_write)
- free(last_write);
- last_write = strdup(buf);
- ++c->pos;
- return (ssize_t)size;
-} /* mock_write */
-
-/* unsupported: int seek(void *cookie, off64_t *offset, int whence) */
-
-static int
-mock_close(void *cookie)
-{
- io_cookie_t *c = cookie;
-
- if (! c)
- return EBADF;
-
- close(c->fd);
- free(c);
- return 0;
-} /* mock_close */
-
-static cookie_io_functions_t mock_io = {
- /* read = */ mock_read,
- /* write = */ mock_write,
- /* seek = */ NULL,
- /* close = */ mock_close,
-};
-
-/*
- * mocked functions
- */
-
-static int myfd = -1;
-
-static void *
-dlopen_libc(void)
-{
- static void *libc = NULL;
-
- if (libc)
- return libc;
-
- libc = dlopen("libc.so.6", RTLD_LAZY);
- if (! libc)
- fail("INTERNAL ERROR: failed to load libc");
- return libc;
-} /* dlopen_libc */
-
-int
-socket(int domain, int __attribute__((unused)) type,
- int __attribute__((unused)) protocol)
-{
- char tmp_file[] = "unixsock_test_socket.XXXXXX";
- int tmp_fd;
-
- /* we only want to mock UNIX sockets; return an error else */
- if (domain != AF_UNIX) {
- errno = EAFNOSUPPORT;
- return -1;
- }
-
- /* create an 'anonymous' file to have a valid file-descriptor
- * which can be close()ed by the caller */
- tmp_fd = mkstemp(tmp_file);
- if (tmp_fd < 0)
- return -1;
-
- unlink(tmp_file);
- myfd = tmp_fd;
- return tmp_fd;
-} /* socket */
-
-int
-connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
-{
- if (sockfd < 0) {
- errno = EBADF;
- return -1;
- }
-
- /* we only want to mock UNIX sockets; return an error else */
- if ((addrlen != sizeof(struct sockaddr_un)) || (! addr)
- || (! ((const struct sockaddr_un *)addr)->sun_path)) {
- errno = EAFNOSUPPORT;
- return -1;
- }
- return 0;
-} /* connect */
-
-FILE *
-fdopen(int fd, const char *mode)
-{
- io_cookie_t *cookie;
-
- if (fd < 0) {
- errno = EBADF;
- return NULL;
- }
-
- /* check also uses fdopen; in that case we need
- * to use the original implementation */
- if (fd != myfd) {
- void *libc = dlopen_libc();
- FILE *(*orig_fdopen)(int, const char *);
-
- orig_fdopen = (FILE *(*)(int, const char *))dlsym(libc, "fdopen");
-
- if (! orig_fdopen)
- fail("INTERNAL ERROR: failed to load fdopen() from libc");
-
- return orig_fdopen(fd, mode);
- }
-
- cookie = calloc(sizeof(*cookie), 1);
- if (! cookie)
- return NULL;
-
- cookie->fd = fd;
- cookie->pos = 0;
- return fopencookie(cookie, mode, mock_io);
-} /* fdopen */
-
-/*
- * private variables
- */
-
-static sdb_unixsock_client_t *client;
-
-static void
-setup(void)
-{
- client = sdb_unixsock_client_create("unixsock_test_path");
- fail_unless(client != NULL,
- "sdb_unixsock_client_create() = NULL; "
- "expected unixsock client object");
-} /* setup */
-
-static void
-teardown(void)
-{
- sdb_unixsock_client_destroy(client);
- client = NULL;
-} /* teardown */
-
-static void
-conn(void)
-{
- int check;
-
- check = sdb_unixsock_client_connect(client);
- fail_unless(check == 0,
- "sdb_unixsock_client_connect() = %i; expected: 0", check);
-} /* conn */
-
-/*
- * tests
- */
-
-START_TEST(test_sdb_unixsock_client_create)
-{
- sdb_unixsock_client_t *c;
- const char *check;
-
- c = sdb_unixsock_client_create(NULL);
- fail_unless(c == NULL,
- "sdb_unixsock_client_create() = %p; expected: NULL", c);
-
- c = sdb_unixsock_client_create("unixsock_test_path");
- fail_unless(c != NULL,
- "sdb_unixsock_client_create() = NULL; "
- "expected unixsock client object");
-
- check = sdb_unixsock_client_path(c);
- fail_unless(check != NULL,
- "sdb_unixsock_client_create() did not store path name");
- fail_unless(!strcmp(check, "unixsock_test_path"),
- "sdb_unixsock_client_create() did not store correct path name; "
- "got: '%s'; expected: 'unixsock_test_path'", check);
- sdb_unixsock_client_destroy(c);
-}
-END_TEST
-
-START_TEST(test_sdb_unixsock_client_connect)
-{
- int check;
-
- check = sdb_unixsock_client_connect(client);
- fail_unless(check == 0,
- "sdb_unixsock_client_connect() = %i; expected: 0", check);
-}
-END_TEST
-
-START_TEST(test_sdb_unixsock_client_send)
-{
- size_t i;
-
- conn();
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- int check;
-
- mock_write_called = 0;
- check = sdb_unixsock_client_send(client, golden_data[i].data);
- /* client_send appends \r\n */
- fail_unless((size_t)check == golden_data[i].len + 2,
- "sdb_unixsock_client_send() = %i; expected: %zu",
- check, golden_data[i].len + 2);
- fail_unless(mock_write_called == 1,
- "sdb_unixsock_client_send() called mock_write %llu times; "
- "expected: 1", mock_write_called);
- fail_unless(last_write != NULL,
- "INTERNAL ERROR: mock_write did not record last write");
- fail_unless((last_write[check - 1] == '\n')
- && (last_write[check - 2] == '\r'),
- "sdb_unixsock_client_send() did not append \\r\\n "
- "before sending; got: '%s'", last_write);
- fail_unless(!strncmp(last_write, golden_data[i].data,
- (size_t)check - 2),
- "sdb_unixsock_client_send() sent unexpected string '%s'; "
- "expected: '%s'", last_write, golden_data[i].data);
- free(last_write);
- last_write = NULL;
- }
-}
-END_TEST
-
-START_TEST(test_sdb_unixsock_client_recv)
-{
- size_t i;
-
- conn();
-
- for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
- char *check;
- char buf[64];
-
- mock_read_called = 0;
- check = sdb_unixsock_client_recv(client, buf, sizeof(buf));
- fail_unless(check != NULL,
- "sdb_unixsock_client_recv() = NULL; expected: a string");
- fail_unless(check == buf,
- "sdb_unixsock_client_recv() did not return a pointer "
- "to the user-provided buffer");
- fail_unless(mock_read_called == 1,
- "sdb_unixsock_client_recv() called mock_read %llu times; "
- "expected: 1", mock_read_called);
- fail_unless(strlen(check) == golden_data[i].len,
- "sdb_unixsock_client_recv() returned string of length "
- "%zu ('%s'); expected: %zu",
- strlen(check), check, golden_data[i].len);
- fail_unless(check[golden_data[i].len] != '\n',
- "sdb_unixsock_client_recv() did not strip newline");
- fail_unless(!strcmp(check, golden_data[i].data),
- "sdb_unixsock_client_recv() = '%s'; expected: '%s'",
- check, golden_data[i].data);
- }
-}
-END_TEST
-
-Suite *
-util_unixsock_suite(void)
-{
- Suite *s = suite_create("utils::unixsock");
- TCase *tc;
-
- tc = tcase_create("core");
- tcase_add_checked_fixture(tc, setup, teardown);
- tcase_add_test(tc, test_sdb_unixsock_client_create);
- tcase_add_test(tc, test_sdb_unixsock_client_connect);
- tcase_add_test(tc, test_sdb_unixsock_client_send);
- tcase_add_test(tc, test_sdb_unixsock_client_recv);
- suite_add_tcase(s, tc);
-
- return s;
-} /* util_unixsock_suite */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-