Code

Moved unit tests into t/unit/ subdirectory.
authorSebastian Harl <sh@tokkee.org>
Sat, 26 Apr 2014 20:42:47 +0000 (22:42 +0200)
committerSebastian Harl <sh@tokkee.org>
Sat, 26 Apr 2014 20:42:47 +0000 (22:42 +0200)
38 files changed:
.gitignore
t/Makefile.am
t/core/data_test.c [deleted file]
t/core/object_test.c [deleted file]
t/core/store_lookup_test.c [deleted file]
t/core/store_test.c [deleted file]
t/core/time_test.c [deleted file]
t/frontend/connection_test.c [deleted file]
t/frontend/parser_test.c [deleted file]
t/frontend/sock_test.c [deleted file]
t/libsysdb_net_test.c [deleted file]
t/libsysdb_test.c [deleted file]
t/libsysdb_test.h [deleted file]
t/libsysdb_testutils.c [deleted file]
t/libsysdb_testutils.h [deleted file]
t/unit/core/data_test.c [new file with mode: 0644]
t/unit/core/object_test.c [new file with mode: 0644]
t/unit/core/store_lookup_test.c [new file with mode: 0644]
t/unit/core/store_test.c [new file with mode: 0644]
t/unit/core/time_test.c [new file with mode: 0644]
t/unit/frontend/connection_test.c [new file with mode: 0644]
t/unit/frontend/parser_test.c [new file with mode: 0644]
t/unit/frontend/sock_test.c [new file with mode: 0644]
t/unit/libsysdb_net_test.c [new file with mode: 0644]
t/unit/libsysdb_test.c [new file with mode: 0644]
t/unit/libsysdb_test.h [new file with mode: 0644]
t/unit/libsysdb_testutils.c [new file with mode: 0644]
t/unit/libsysdb_testutils.h [new file with mode: 0644]
t/unit/utils/channel_test.c [new file with mode: 0644]
t/unit/utils/dbi_test.c [new file with mode: 0644]
t/unit/utils/llist_test.c [new file with mode: 0644]
t/unit/utils/strbuf_test.c [new file with mode: 0644]
t/unit/utils/unixsock_test.c [new file with mode: 0644]
t/utils/channel_test.c [deleted file]
t/utils/dbi_test.c [deleted file]
t/utils/llist_test.c [deleted file]
t/utils/strbuf_test.c [deleted file]
t/utils/unixsock_test.c [deleted file]

index 9f1fd8ff4e3ca14646e8733b29d48b78738fa7d2..0657cd0d112e8ac2404ed3a098785c4c5c9d1085 100644 (file)
@@ -49,8 +49,8 @@ src/sysdbd
 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
index 551d8639025caa351361d98a5d2c0408dc953f0f..4764a6ed276d5fdd9a71469975816f75388dd5e3 100644 (file)
@@ -5,35 +5,35 @@ AM_CFLAGS = @STRICT_CFLAGS@ @COVERAGE_CFLAGS@
 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
deleted file mode 100644 (file)
index 261d5fd..0000000
+++ /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
deleted file mode 100644 (file)
index 85e95c3..0000000
+++ /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
deleted file mode 100644 (file)
index 8ead947..0000000
+++ /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
deleted file mode 100644 (file)
index 29c5b1b..0000000
+++ /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
deleted file mode 100644 (file)
index f22e236..0000000
+++ /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
deleted file mode 100644 (file)
index 9851a86..0000000
+++ /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
deleted file mode 100644 (file)
index f64b9eb..0000000
+++ /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
deleted file mode 100644 (file)
index 55f1a0c..0000000
+++ /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
deleted file mode 100644 (file)
index 85ec83c..0000000
+++ /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
deleted file mode 100644 (file)
index 96bb907..0000000
+++ /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
deleted file mode 100644 (file)
index 8d98479..0000000
+++ /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
deleted file mode 100644 (file)
index 964afdb..0000000
+++ /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(&reg, regex, REG_EXTENDED | REG_NOSUB);
-       if (status)
-               return status;
-
-       status = regexec(&reg, string, /* matches = */ 0, NULL, /* flags = */ 0);
-       regfree(&reg);
-       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
deleted file mode 100644 (file)
index 3f828a9..0000000
+++ /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
new file mode 100644 (file)
index 0000000..432bd75
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..dec6d41
--- /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
new file mode 100644 (file)
index 0000000..5631519
--- /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
new file mode 100644 (file)
index 0000000..07651ee
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..2bffbbd
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..125bb56
--- /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
new file mode 100644 (file)
index 0000000..cb3a2ad
--- /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
new file mode 100644 (file)
index 0000000..852a498
--- /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
new file mode 100644 (file)
index 0000000..ae213c0
--- /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
new file mode 100644 (file)
index 0000000..7d833d5
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..df5b904
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..6f6692d
--- /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(&reg, regex, REG_EXTENDED | REG_NOSUB);
+       if (status)
+               return status;
+
+       status = regexec(&reg, string, /* matches = */ 0, NULL, /* flags = */ 0);
+       regfree(&reg);
+       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
new file mode 100644 (file)
index 0000000..63ef460
--- /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
new file mode 100644 (file)
index 0000000..0354397
--- /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
new file mode 100644 (file)
index 0000000..b8a5ea9
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..5ae771f
--- /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
new file mode 100644 (file)
index 0000000..f9c3e93
--- /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].i