Code

store: Added sdb_store_iterate().
[sysdb.git] / t / core / store_test.c
1 /*
2  * SysDB - t/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 "libsysdb_test.h"
31 #include <check.h>
32 #include <string.h>
34 static void
35 populate(void)
36 {
37         sdb_data_t datum;
39         sdb_store_host("h1", 1);
40         sdb_store_host("h2", 1);
42         datum.type = SDB_TYPE_STRING;
43         datum.data.string = "v1";
44         sdb_store_attribute("h1", "k1", &datum, 1);
45         datum.data.string = "v2";
46         sdb_store_attribute("h1", "k2", &datum, 1);
47         datum.data.string = "v3";
48         sdb_store_attribute("h1", "k3", &datum, 1);
50         sdb_store_service("h2", "s1", 1);
51         sdb_store_service("h2", "s2", 1);
52 } /* populate */
54 START_TEST(test_store_host)
55 {
56         struct {
57                 const char *name;
58                 sdb_time_t  last_update;
59                 int         expected;
60         } golden_data[] = {
61                 { "a", 2, 0 },
62                 { "a", 3, 0 },
63                 { "a", 1, 1 },
64                 { "b", 2, 0 },
65                 { "b", 1, 1 },
66                 { "A", 1, 1 }, /* case-insensitive */
67                 { "A", 4, 0 },
68         };
70         struct {
71                 const char *name;
72                 _Bool       has;
73         } golden_hosts[] = {
74                 { "a", 1 == 1 },
75                 { "b", 1 == 1 },
76                 { "c", 0 == 1 },
77                 { "A", 1 == 1 },
78         };
80         size_t i;
82         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
83                 int status;
85                 status = sdb_store_host(golden_data[i].name,
86                                 golden_data[i].last_update);
87                 fail_unless(status == golden_data[i].expected,
88                                 "sdb_store_host(%s, %d) = %d; expected: %d",
89                                 golden_data[i].name, (int)golden_data[i].last_update,
90                                 status, golden_data[i].expected);
91         }
93         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_hosts); ++i) {
94                 _Bool has;
96                 has = sdb_store_has_host(golden_hosts[i].name);
97                 fail_unless(has == golden_hosts[i].has,
98                                 "sdb_store_has_host(%s) = %d; expected: %d",
99                                 golden_hosts[i].name, has, golden_hosts[i].has);
100         }
102 END_TEST
104 START_TEST(test_store_get_host)
106         char *golden_hosts[] = { "a", "b", "c" };
107         char *unknown_hosts[] = { "x", "y", "z" };
108         size_t i;
110         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_hosts); ++i) {
111                 int status = sdb_store_host(golden_hosts[i], 1);
112                 fail_unless(status >= 0,
113                                 "sdb_store_host(%s) = %d; expected: >=0",
114                                 golden_hosts[i], status);
115         }
117         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_hosts); ++i) {
118                 sdb_store_base_t *sobj1, *sobj2;
119                 int ref_cnt;
121                 fail_unless(sdb_store_has_host(golden_hosts[i]),
122                                 "sdb_store_has_host(%s) = FALSE; expected: TRUE",
123                                 golden_hosts[i]);
125                 sobj1 = sdb_store_get_host(golden_hosts[i]);
126                 fail_unless(sobj1 != NULL,
127                                 "sdb_store_get_host(%s) = NULL; expected: <host>",
128                                 golden_hosts[i]);
129                 ref_cnt = SDB_OBJ(sobj1)->ref_cnt;
131                 fail_unless(ref_cnt > 1,
132                                 "sdb_store_get_host(%s) did not increment ref count: "
133                                 "got: %d; expected: >1", golden_hosts[i], ref_cnt);
135                 sobj2 = sdb_store_get_host(golden_hosts[i]);
136                 fail_unless(sobj2 != NULL,
137                                 "sdb_store_get_host(%s) = NULL; expected: <host>",
138                                 golden_hosts[i]);
140                 fail_unless(sobj1 == sobj2,
141                                 "sdb_store_get_host(%s) returned different objects "
142                                 "in successive calls", golden_hosts[i]);
143                 fail_unless(SDB_OBJ(sobj2)->ref_cnt == ref_cnt + 1,
144                                 "sdb_store_get_hosts(%s) did not increment ref count "
145                                 "(first call: %d; second call: %d)",
146                                 golden_hosts[i], ref_cnt, SDB_OBJ(sobj2)->ref_cnt);
148                 sdb_object_deref(SDB_OBJ(sobj1));
149                 sdb_object_deref(SDB_OBJ(sobj2));
150         }
151         for (i = 0; i < SDB_STATIC_ARRAY_LEN(unknown_hosts); ++i) {
152                 sdb_store_base_t *sobj;
154                 fail_unless(!sdb_store_has_host(unknown_hosts[i]),
155                                 "sdb_store_has_host(%s) = TRUE; expected: FALSE",
156                                 unknown_hosts[i]);
158                 sobj = sdb_store_get_host(unknown_hosts[i]);
159                 fail_unless(!sobj, "sdb_store_get_host(%s) = <host:%s>; expected: NULL",
160                                 unknown_hosts[i], sobj ? SDB_OBJ(sobj)->name : "NULL");
161         }
163 END_TEST
165 START_TEST(test_store_attr)
167         struct {
168                 const char *host;
169                 const char *key;
170                 char       *value;
171                 sdb_time_t  last_update;
172                 int         expected;
173         } golden_data[] = {
174                 { "k", "k", "v", 1, -1 },
175                 { "k", "k", "v", 1, -1 }, /* retry to ensure the host is not created */
176                 { "l", "k1", "v1", 1, 0 },
177                 { "l", "k1", "v2", 2, 0 },
178                 { "l", "k1", "v3", 1, 1 },
179                 { "l", "k2", "v1", 1, 0 },
180                 { "m", "k", "v1", 2, 0 },
181                 { "m", "k", "v2", 1, 1 },
182         };
184         size_t i;
186         sdb_store_host("l", 1);
187         sdb_store_host("m", 1);
188         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
189                 sdb_data_t datum;
190                 int status;
192                 /* XXX: test other types as well */
193                 datum.type = SDB_TYPE_STRING;
194                 datum.data.string = golden_data[i].value;
196                 status = sdb_store_attribute(golden_data[i].host,
197                                 golden_data[i].key, &datum,
198                                 golden_data[i].last_update);
199                 fail_unless(status == golden_data[i].expected,
200                                 "sdb_store_attribute(%s, %s, %s, %d) = %d; expected: %d",
201                                 golden_data[i].host, golden_data[i].key, golden_data[i].value,
202                                 golden_data[i].last_update, status, golden_data[i].expected);
203         }
205 END_TEST
207 START_TEST(test_store_service)
209         struct {
210                 const char *host;
211                 const char *svc;
212                 sdb_time_t  last_update;
213                 int         expected;
214         } golden_data[] = {
215                 { "k", "s", 1, -1 },
216                 { "k", "s", 1, -1 }, /* retry to ensure the host is not created */
217                 { "l", "s1", 1, 0 },
218                 { "l", "s1", 2, 0 },
219                 { "l", "s1", 1, 1 },
220                 { "l", "s2", 1, 0 },
221                 { "m", "s", 2, 0 },
222                 { "m", "s", 1, 1 },
223         };
225         size_t i;
227         sdb_store_host("m", 1);
228         sdb_store_host("l", 1);
229         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
230                 int status;
232                 status = sdb_store_service(golden_data[i].host,
233                                 golden_data[i].svc, golden_data[i].last_update);
234                 fail_unless(status == golden_data[i].expected,
235                                 "sdb_store_service(%s, %s, %d) = %d; expected: %d",
236                                 golden_data[i].host, golden_data[i].svc,
237                                 golden_data[i].last_update, status, golden_data[i].expected);
238         }
240 END_TEST
242 static void
243 verify_json_output(sdb_strbuf_t *buf, const char *expected, int flags)
245         int pos;
246         size_t len1, len2;
247         size_t i;
249         len1 = strlen(sdb_strbuf_string(buf));
250         len2 = strlen(expected);
252         pos = -1;
253         if (len1 != len2)
254                 pos = (int)(len1 <= len2 ? len1 : len2);
256         for (i = 0; i < (len1 <= len2 ? len1 : len2); ++i) {
257                 if (sdb_strbuf_string(buf)[i] != expected[i]) {
258                         pos = (int)i;
259                         break;
260                 }
261         }
263         fail_unless(pos == -1,
264                         "sdb_store_tojson(%x) returned unexpected result\n"
265                         "         got: %s\n              %*s\n    expected: %s",
266                         flags, sdb_strbuf_string(buf), pos + 1, "^", expected);
267 } /* verify_json_output */
269 START_TEST(test_store_tojson)
271         sdb_strbuf_t *buf;
272         size_t i;
274         struct {
275                 int flags;
276                 const char *expected;
277         } golden_data[] = {
278                 { 0, "{\"hosts\":["
279                                 "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
280                                         "\"attributes\": ["
281                                                 "{\"name\": \"k1\", \"value\": \"v1\", \"last_update\": \"1970-01-01 00:00:00 +0000\"},"
282                                                 "{\"name\": \"k2\", \"value\": \"v2\", \"last_update\": \"1970-01-01 00:00:00 +0000\"},"
283                                                 "{\"name\": \"k3\", \"value\": \"v3\", \"last_update\": \"1970-01-01 00:00:00 +0000\"}"
284                                         "], "
285                                         "\"services\": []},"
286                                 "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
287                                         "\"attributes\": [], "
288                                         "\"services\": ["
289                                                 "{\"name\": \"s1\", \"last_update\": \"1970-01-01 00:00:00 +0000\"},"
290                                                 "{\"name\": \"s2\", \"last_update\": \"1970-01-01 00:00:00 +0000\"}"
291                                         "]}"
292                         "]}" },
293                 { SDB_SKIP_SERVICES,
294                         "{\"hosts\":["
295                                 "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
296                                         "\"attributes\": ["
297                                                 "{\"name\": \"k1\", \"value\": \"v1\", \"last_update\": \"1970-01-01 00:00:00 +0000\"},"
298                                                 "{\"name\": \"k2\", \"value\": \"v2\", \"last_update\": \"1970-01-01 00:00:00 +0000\"},"
299                                                 "{\"name\": \"k3\", \"value\": \"v3\", \"last_update\": \"1970-01-01 00:00:00 +0000\"}"
300                                         "]},"
301                                 "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
302                                         "\"attributes\": []}"
303                         "]}" },
304                 { SDB_SKIP_ATTRIBUTES,
305                         "{\"hosts\":["
306                                 "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
307                                         "\"services\": []},"
308                                 "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
309                                         "\"services\": ["
310                                                 "{\"name\": \"s1\", \"last_update\": \"1970-01-01 00:00:00 +0000\"},"
311                                                 "{\"name\": \"s2\", \"last_update\": \"1970-01-01 00:00:00 +0000\"}"
312                                         "]}"
313                         "]}" },
314                 { SDB_SKIP_SERVICES | SDB_SKIP_ATTRIBUTES,
315                         "{\"hosts\":["
316                                 "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\"},"
317                                 "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\"}"
318                         "]}" },
319         };
321         buf = sdb_strbuf_create(0);
322         fail_unless(buf != NULL, "INTERNAL ERROR: failed to create string buffer");
323         populate();
325         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
326                 int status;
328                 sdb_strbuf_clear(buf);
330                 status = sdb_store_tojson(buf, golden_data[i].flags);
331                 fail_unless(status == 0,
332                                 "sdb_store_tojson(%x) = %d; expected: 0",
333                                 golden_data[i].flags, status);
335                 verify_json_output(buf, golden_data[i].expected, golden_data[i].flags);
336         }
337         sdb_strbuf_destroy(buf);
339 END_TEST
341 static int
342 iter_incr(sdb_store_base_t *obj, void *user_data)
344         intptr_t *i = user_data;
346         fail_unless(obj != NULL,
347                         "sdb_store_iterate callback received NULL obj; expected: "
348                         "<store base obj>");
349         fail_unless(i != NULL,
350                         "sdb_store_iterate callback received NULL user_data; "
351                         "expected: <pointer to data>");
353         ++(*i);
354         return 0;
355 } /* iter_incr */
357 static int
358 iter_error(sdb_store_base_t *obj, void *user_data)
360         intptr_t *i = user_data;
362         fail_unless(obj != NULL,
363                         "sdb_store_iterate callback received NULL obj; expected: "
364                         "<store base obj>");
365         fail_unless(i != NULL,
366                         "sdb_store_iterate callback received NULL user_data; "
367                         "expected: <pointer to data>");
369         ++(*i);
370         return -1;
371 } /* iter_error */
373 START_TEST(test_iterate)
375         intptr_t i = 0;
376         int check;
378         /* empty store */
379         check = sdb_store_iterate(iter_incr, &i);
380         fail_unless(check == -1,
381                         "sdb_store_iterate(), empty store = %d; expected: -1", check);
382         fail_unless(i == 0,
383                         "sdb_store_iterate called callback %d times; expected: 0", (int)i);
385         populate();
387         check = sdb_store_iterate(iter_incr, &i);
388         fail_unless(check == 0,
389                         "sdb_store_iterate() = %d; expected: 0", check);
390         fail_unless(i == 2,
391                         "sdb_store_iterate called callback %d times; expected: 1", (int)i);
393         i = 0;
394         check = sdb_store_iterate(iter_error, &i);
395         fail_unless(check == -1,
396                         "sdb_store_iterate(), error callback = %d; expected: -1", check);
397         fail_unless(i == 1,
398                         "sdb_store_iterate called callback %d times "
399                         "(callback returned error); expected: 1", (int)i);
401 END_TEST
403 Suite *
404 core_store_suite(void)
406         Suite *s = suite_create("core::store");
407         TCase *tc;
409         tc = tcase_create("core");
410         tcase_add_test(tc, test_store_tojson);
411         tcase_add_test(tc, test_store_host);
412         tcase_add_test(tc, test_store_get_host);
413         tcase_add_test(tc, test_store_attr);
414         tcase_add_test(tc, test_store_service);
415         tcase_add_test(tc, test_iterate);
416         tcase_add_unchecked_fixture(tc, NULL, sdb_store_clear);
417         suite_add_tcase(s, tc);
419         return s;
420 } /* core_store_suite */
422 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */