Code

Merged branch 'master' of git://git.tokkee.org/sysdb.
[sysdb.git] / t / unit / core / store_test.c
1 /*
2  * SysDB - t/unit/core/store_test.c
3  * Copyright (C) 2013 Sebastian 'tokkee' Harl <sh@tokkee.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
28 #include "core/store.h"
29 #include "core/store-private.h"
30 #include "libsysdb_test.h"
32 #include <check.h>
33 #include <string.h>
35 static void
36 populate(void)
37 {
38         sdb_data_t datum;
40         sdb_store_host("h1", 1);
41         sdb_store_host("h2", 1);
43         datum.type = SDB_TYPE_STRING;
44         datum.data.string = "v1";
45         sdb_store_attribute("h1", "k1", &datum, 1);
46         datum.data.string = "v2";
47         sdb_store_attribute("h1", "k2", &datum, 1);
48         datum.data.string = "v3";
49         sdb_store_attribute("h1", "k3", &datum, 1);
51         sdb_store_service("h2", "s1", 1);
52         sdb_store_service("h2", "s2", 1);
53 } /* populate */
55 START_TEST(test_store_host)
56 {
57         struct {
58                 const char *name;
59                 sdb_time_t  last_update;
60                 int         expected;
61         } golden_data[] = {
62                 { "a", 2, 0 },
63                 { "a", 3, 0 },
64                 { "a", 1, 1 },
65                 { "b", 2, 0 },
66                 { "b", 1, 1 },
67                 { "A", 1, 1 }, /* case-insensitive */
68                 { "A", 4, 0 },
69         };
71         struct {
72                 const char *name;
73                 _Bool       has;
74         } golden_hosts[] = {
75                 { "a", 1 == 1 },
76                 { "b", 1 == 1 },
77                 { "c", 0 == 1 },
78                 { "A", 1 == 1 },
79         };
81         size_t i;
83         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
84                 int status;
86                 status = sdb_store_host(golden_data[i].name,
87                                 golden_data[i].last_update);
88                 fail_unless(status == golden_data[i].expected,
89                                 "sdb_store_host(%s, %d) = %d; expected: %d",
90                                 golden_data[i].name, (int)golden_data[i].last_update,
91                                 status, golden_data[i].expected);
92         }
94         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_hosts); ++i) {
95                 _Bool has;
97                 has = sdb_store_has_host(golden_hosts[i].name);
98                 fail_unless(has == golden_hosts[i].has,
99                                 "sdb_store_has_host(%s) = %d; expected: %d",
100                                 golden_hosts[i].name, has, golden_hosts[i].has);
101         }
103 END_TEST
105 START_TEST(test_store_get_host)
107         char *golden_hosts[] = { "a", "b", "c" };
108         char *unknown_hosts[] = { "x", "y", "z" };
109         size_t i;
111         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_hosts); ++i) {
112                 int status = sdb_store_host(golden_hosts[i], 1);
113                 fail_unless(status >= 0,
114                                 "sdb_store_host(%s) = %d; expected: >=0",
115                                 golden_hosts[i], status);
116         }
118         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_hosts); ++i) {
119                 sdb_store_obj_t *sobj1, *sobj2;
120                 int ref_cnt;
122                 fail_unless(sdb_store_has_host(golden_hosts[i]),
123                                 "sdb_store_has_host(%s) = FALSE; expected: TRUE",
124                                 golden_hosts[i]);
126                 sobj1 = sdb_store_get_host(golden_hosts[i]);
127                 fail_unless(sobj1 != NULL,
128                                 "sdb_store_get_host(%s) = NULL; expected: <host>",
129                                 golden_hosts[i]);
130                 ref_cnt = SDB_OBJ(sobj1)->ref_cnt;
132                 fail_unless(ref_cnt > 1,
133                                 "sdb_store_get_host(%s) did not increment ref count: "
134                                 "got: %d; expected: >1", golden_hosts[i], ref_cnt);
136                 sobj2 = sdb_store_get_host(golden_hosts[i]);
137                 fail_unless(sobj2 != NULL,
138                                 "sdb_store_get_host(%s) = NULL; expected: <host>",
139                                 golden_hosts[i]);
141                 fail_unless(sobj1 == sobj2,
142                                 "sdb_store_get_host(%s) returned different objects "
143                                 "in successive calls", golden_hosts[i]);
144                 fail_unless(SDB_OBJ(sobj2)->ref_cnt == ref_cnt + 1,
145                                 "sdb_store_get_hosts(%s) did not increment ref count "
146                                 "(first call: %d; second call: %d)",
147                                 golden_hosts[i], ref_cnt, SDB_OBJ(sobj2)->ref_cnt);
149                 sdb_object_deref(SDB_OBJ(sobj1));
150                 sdb_object_deref(SDB_OBJ(sobj2));
151         }
152         for (i = 0; i < SDB_STATIC_ARRAY_LEN(unknown_hosts); ++i) {
153                 sdb_store_obj_t *sobj;
155                 fail_unless(!sdb_store_has_host(unknown_hosts[i]),
156                                 "sdb_store_has_host(%s) = TRUE; expected: FALSE",
157                                 unknown_hosts[i]);
159                 sobj = sdb_store_get_host(unknown_hosts[i]);
160                 fail_unless(!sobj, "sdb_store_get_host(%s) = <host:%s>; expected: NULL",
161                                 unknown_hosts[i], sobj ? SDB_OBJ(sobj)->name : "NULL");
162         }
164 END_TEST
166 START_TEST(test_store_attr)
168         struct {
169                 const char *host;
170                 const char *key;
171                 char       *value;
172                 sdb_time_t  last_update;
173                 int         expected;
174         } golden_data[] = {
175                 { "k", "k", "v", 1, -1 },
176                 { "k", "k", "v", 1, -1 }, /* retry to ensure the host is not created */
177                 { "l", "k1", "v1", 1, 0 },
178                 { "l", "k1", "v2", 2, 0 },
179                 { "l", "k1", "v3", 1, 1 },
180                 { "l", "k2", "v1", 1, 0 },
181                 { "m", "k", "v1", 2, 0 },
182                 { "m", "k", "v2", 1, 1 },
183         };
185         size_t i;
187         sdb_store_host("l", 1);
188         sdb_store_host("m", 1);
189         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
190                 sdb_data_t datum;
191                 int status;
193                 /* XXX: test other types as well */
194                 datum.type = SDB_TYPE_STRING;
195                 datum.data.string = golden_data[i].value;
197                 status = sdb_store_attribute(golden_data[i].host,
198                                 golden_data[i].key, &datum,
199                                 golden_data[i].last_update);
200                 fail_unless(status == golden_data[i].expected,
201                                 "sdb_store_attribute(%s, %s, %s, %d) = %d; expected: %d",
202                                 golden_data[i].host, golden_data[i].key, golden_data[i].value,
203                                 golden_data[i].last_update, status, golden_data[i].expected);
204         }
206 END_TEST
208 START_TEST(test_store_service)
210         struct {
211                 const char *host;
212                 const char *svc;
213                 sdb_time_t  last_update;
214                 int         expected;
215         } golden_data[] = {
216                 { "k", "s", 1, -1 },
217                 { "k", "s", 1, -1 }, /* retry to ensure the host is not created */
218                 { "l", "s1", 1, 0 },
219                 { "l", "s1", 2, 0 },
220                 { "l", "s1", 1, 1 },
221                 { "l", "s2", 1, 0 },
222                 { "m", "s", 2, 0 },
223                 { "m", "s", 1, 1 },
224         };
226         size_t i;
228         sdb_store_host("m", 1);
229         sdb_store_host("l", 1);
230         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
231                 int status;
233                 status = sdb_store_service(golden_data[i].host,
234                                 golden_data[i].svc, golden_data[i].last_update);
235                 fail_unless(status == golden_data[i].expected,
236                                 "sdb_store_service(%s, %s, %d) = %d; expected: %d",
237                                 golden_data[i].host, golden_data[i].svc,
238                                 golden_data[i].last_update, status, golden_data[i].expected);
239         }
241 END_TEST
243 static void
244 verify_json_output(sdb_strbuf_t *buf, const char *expected, int flags)
246         int pos;
247         size_t len1, len2;
248         size_t i;
250         len1 = strlen(sdb_strbuf_string(buf));
251         len2 = strlen(expected);
253         pos = -1;
254         if (len1 != len2)
255                 pos = (int)(len1 <= len2 ? len1 : len2);
257         for (i = 0; i < (len1 <= len2 ? len1 : len2); ++i) {
258                 if (sdb_strbuf_string(buf)[i] != expected[i]) {
259                         pos = (int)i;
260                         break;
261                 }
262         }
264         fail_unless(pos == -1,
265                         "sdb_store_tojson(%x) returned unexpected result\n"
266                         "         got: %s\n              %*s\n    expected: %s",
267                         flags, sdb_strbuf_string(buf), pos + 1, "^", expected);
268 } /* verify_json_output */
270 START_TEST(test_store_tojson)
272         sdb_strbuf_t *buf;
273         size_t i;
275         struct {
276                 int flags;
277                 const char *expected;
278         } golden_data[] = {
279                 { 0, "{\"hosts\":["
280                                 "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
281                                         "\"update_interval\": \"0s\", "
282                                         "\"attributes\": ["
283                                                 "{\"name\": \"k1\", \"value\": \"v1\", "
284                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
285                                                         "\"update_interval\": \"0s\"},"
286                                                 "{\"name\": \"k2\", \"value\": \"v2\", "
287                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
288                                                         "\"update_interval\": \"0s\"},"
289                                                 "{\"name\": \"k3\", \"value\": \"v3\", "
290                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
291                                                         "\"update_interval\": \"0s\"}"
292                                         "], "
293                                         "\"services\": []},"
294                                 "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
295                                         "\"update_interval\": \"0s\", "
296                                         "\"attributes\": [], "
297                                         "\"services\": ["
298                                                 "{\"name\": \"s1\", "
299                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
300                                                         "\"update_interval\": \"0s\"},"
301                                                 "{\"name\": \"s2\", "
302                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
303                                                         "\"update_interval\": \"0s\"}"
304                                         "]}"
305                         "]}" },
306                 { SDB_SKIP_SERVICES,
307                         "{\"hosts\":["
308                                 "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
309                                         "\"update_interval\": \"0s\", "
310                                         "\"attributes\": ["
311                                                 "{\"name\": \"k1\", \"value\": \"v1\", "
312                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
313                                                         "\"update_interval\": \"0s\"},"
314                                                 "{\"name\": \"k2\", \"value\": \"v2\", "
315                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
316                                                         "\"update_interval\": \"0s\"},"
317                                                 "{\"name\": \"k3\", \"value\": \"v3\", "
318                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
319                                                         "\"update_interval\": \"0s\"}"
320                                         "]},"
321                                 "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
322                                         "\"update_interval\": \"0s\", "
323                                         "\"attributes\": []}"
324                         "]}" },
325                 { SDB_SKIP_ATTRIBUTES,
326                         "{\"hosts\":["
327                                 "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
328                                         "\"update_interval\": \"0s\", "
329                                         "\"services\": []},"
330                                 "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
331                                         "\"update_interval\": \"0s\", "
332                                         "\"services\": ["
333                                                 "{\"name\": \"s1\", "
334                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
335                                                         "\"update_interval\": \"0s\"},"
336                                                 "{\"name\": \"s2\", "
337                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
338                                                         "\"update_interval\": \"0s\"}"
339                                         "]}"
340                         "]}" },
341                 { SDB_SKIP_SERVICES | SDB_SKIP_ATTRIBUTES,
342                         "{\"hosts\":["
343                                 "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
344                                         "\"update_interval\": \"0s\"},"
345                                 "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
346                                         "\"update_interval\": \"0s\"}"
347                         "]}" },
348         };
350         buf = sdb_strbuf_create(0);
351         fail_unless(buf != NULL, "INTERNAL ERROR: failed to create string buffer");
352         populate();
354         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
355                 int status;
357                 sdb_strbuf_clear(buf);
359                 status = sdb_store_tojson(buf, golden_data[i].flags);
360                 fail_unless(status == 0,
361                                 "sdb_store_tojson(%x) = %d; expected: 0",
362                                 golden_data[i].flags, status);
364                 verify_json_output(buf, golden_data[i].expected, golden_data[i].flags);
365         }
366         sdb_strbuf_destroy(buf);
368 END_TEST
370 START_TEST(test_interval)
372         sdb_store_obj_t *host;
374         /* 10 us interval */
375         sdb_store_host("host", 10);
376         sdb_store_host("host", 20);
377         sdb_store_host("host", 30);
378         sdb_store_host("host", 40);
380         host = sdb_store_get_host("host");
381         fail_unless(host != NULL,
382                         "INTERNAL ERROR: store doesn't have host after adding it");
384         fail_unless(host->interval == 10,
385                         "sdb_store_host() did not calculate interval correctly: "
386                         "got: %"PRIscTIME"; expected: %"PRIscTIME, host->interval, 10);
388         /* multiple updates for the same timestamp don't modify the interval */
389         sdb_store_host("host", 40);
390         sdb_store_host("host", 40);
391         sdb_store_host("host", 40);
392         sdb_store_host("host", 40);
394         fail_unless(host->interval == 10,
395                         "sdb_store_host() changed interval when doing multiple updates "
396                         "using the same timestamp; got: %"PRIscTIME"; "
397                         "expected: %"PRIscTIME, host->interval, 10);
399         /* multiple updates using an timestamp don't modify the interval */
400         sdb_store_host("host", 20);
401         sdb_store_host("host", 20);
402         sdb_store_host("host", 20);
403         sdb_store_host("host", 20);
405         fail_unless(host->interval == 10,
406                         "sdb_store_host() changed interval when doing multiple updates "
407                         "using an old timestamp; got: %"PRIscTIME"; expected: %"PRIscTIME,
408                         host->interval, 10);
410         /* new interval: 20 us */
411         sdb_store_host("host", 60);
412         fail_unless(host->interval == 11,
413                         "sdb_store_host() did not calculate interval correctly: "
414                         "got: %"PRIscTIME"; expected: %"PRIscTIME, host->interval, 11);
416         /* new interval: 40 us */
417         sdb_store_host("host", 100);
418         fail_unless(host->interval == 13,
419                         "sdb_store_host() did not calculate interval correctly: "
420                         "got: %"PRIscTIME"; expected: %"PRIscTIME, host->interval, 11);
422         sdb_object_deref(SDB_OBJ(host));
424 END_TEST
426 static int
427 iter_incr(sdb_store_obj_t *obj, void *user_data)
429         intptr_t *i = user_data;
431         fail_unless(obj != NULL,
432                         "sdb_store_iterate callback received NULL obj; expected: "
433                         "<store base obj>");
434         fail_unless(i != NULL,
435                         "sdb_store_iterate callback received NULL user_data; "
436                         "expected: <pointer to data>");
438         ++(*i);
439         return 0;
440 } /* iter_incr */
442 static int
443 iter_error(sdb_store_obj_t *obj, void *user_data)
445         intptr_t *i = user_data;
447         fail_unless(obj != NULL,
448                         "sdb_store_iterate callback received NULL obj; expected: "
449                         "<store base obj>");
450         fail_unless(i != NULL,
451                         "sdb_store_iterate callback received NULL user_data; "
452                         "expected: <pointer to data>");
454         ++(*i);
455         return -1;
456 } /* iter_error */
458 START_TEST(test_iterate)
460         intptr_t i = 0;
461         int check;
463         /* empty store */
464         check = sdb_store_iterate(iter_incr, &i);
465         fail_unless(check == -1,
466                         "sdb_store_iterate(), empty store = %d; expected: -1", check);
467         fail_unless(i == 0,
468                         "sdb_store_iterate called callback %d times; expected: 0", (int)i);
470         populate();
472         check = sdb_store_iterate(iter_incr, &i);
473         fail_unless(check == 0,
474                         "sdb_store_iterate() = %d; expected: 0", check);
475         fail_unless(i == 2,
476                         "sdb_store_iterate called callback %d times; expected: 1", (int)i);
478         i = 0;
479         check = sdb_store_iterate(iter_error, &i);
480         fail_unless(check == -1,
481                         "sdb_store_iterate(), error callback = %d; expected: -1", check);
482         fail_unless(i == 1,
483                         "sdb_store_iterate called callback %d times "
484                         "(callback returned error); expected: 1", (int)i);
486 END_TEST
488 Suite *
489 core_store_suite(void)
491         Suite *s = suite_create("core::store");
492         TCase *tc;
494         tc = tcase_create("core");
495         tcase_add_test(tc, test_store_tojson);
496         tcase_add_test(tc, test_store_host);
497         tcase_add_test(tc, test_store_get_host);
498         tcase_add_test(tc, test_store_attr);
499         tcase_add_test(tc, test_store_service);
500         tcase_add_test(tc, test_interval);
501         tcase_add_test(tc, test_iterate);
502         tcase_add_unchecked_fixture(tc, NULL, sdb_store_clear);
503         suite_add_tcase(s, tc);
505         return s;
506 } /* core_store_suite */
508 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */