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 }
101 }
102 END_TEST
104 START_TEST(test_store_get_host)
105 {
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 }
162 }
163 END_TEST
165 START_TEST(test_store_attr)
166 {
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 }
204 }
205 END_TEST
207 START_TEST(test_store_service)
208 {
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 }
239 }
240 END_TEST
242 static void
243 verify_json_output(sdb_strbuf_t *buf, const char *expected, int flags)
244 {
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)
270 {
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 "\"update_interval\": \"0s\", "
281 "\"attributes\": ["
282 "{\"name\": \"k1\", \"value\": \"v1\", "
283 "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
284 "\"update_interval\": \"0s\"},"
285 "{\"name\": \"k2\", \"value\": \"v2\", "
286 "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
287 "\"update_interval\": \"0s\"},"
288 "{\"name\": \"k3\", \"value\": \"v3\", "
289 "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
290 "\"update_interval\": \"0s\"}"
291 "], "
292 "\"services\": []},"
293 "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
294 "\"update_interval\": \"0s\", "
295 "\"attributes\": [], "
296 "\"services\": ["
297 "{\"name\": \"s1\", "
298 "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
299 "\"update_interval\": \"0s\"},"
300 "{\"name\": \"s2\", "
301 "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
302 "\"update_interval\": \"0s\"}"
303 "]}"
304 "]}" },
305 { SDB_SKIP_SERVICES,
306 "{\"hosts\":["
307 "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
308 "\"update_interval\": \"0s\", "
309 "\"attributes\": ["
310 "{\"name\": \"k1\", \"value\": \"v1\", "
311 "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
312 "\"update_interval\": \"0s\"},"
313 "{\"name\": \"k2\", \"value\": \"v2\", "
314 "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
315 "\"update_interval\": \"0s\"},"
316 "{\"name\": \"k3\", \"value\": \"v3\", "
317 "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
318 "\"update_interval\": \"0s\"}"
319 "]},"
320 "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
321 "\"update_interval\": \"0s\", "
322 "\"attributes\": []}"
323 "]}" },
324 { SDB_SKIP_ATTRIBUTES,
325 "{\"hosts\":["
326 "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
327 "\"update_interval\": \"0s\", "
328 "\"services\": []},"
329 "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
330 "\"update_interval\": \"0s\", "
331 "\"services\": ["
332 "{\"name\": \"s1\", "
333 "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
334 "\"update_interval\": \"0s\"},"
335 "{\"name\": \"s2\", "
336 "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
337 "\"update_interval\": \"0s\"}"
338 "]}"
339 "]}" },
340 { SDB_SKIP_SERVICES | SDB_SKIP_ATTRIBUTES,
341 "{\"hosts\":["
342 "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
343 "\"update_interval\": \"0s\"},"
344 "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
345 "\"update_interval\": \"0s\"}"
346 "]}" },
347 };
349 buf = sdb_strbuf_create(0);
350 fail_unless(buf != NULL, "INTERNAL ERROR: failed to create string buffer");
351 populate();
353 for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
354 int status;
356 sdb_strbuf_clear(buf);
358 status = sdb_store_tojson(buf, golden_data[i].flags);
359 fail_unless(status == 0,
360 "sdb_store_tojson(%x) = %d; expected: 0",
361 golden_data[i].flags, status);
363 verify_json_output(buf, golden_data[i].expected, golden_data[i].flags);
364 }
365 sdb_strbuf_destroy(buf);
366 }
367 END_TEST
369 static int
370 iter_incr(sdb_store_base_t *obj, void *user_data)
371 {
372 intptr_t *i = user_data;
374 fail_unless(obj != NULL,
375 "sdb_store_iterate callback received NULL obj; expected: "
376 "<store base obj>");
377 fail_unless(i != NULL,
378 "sdb_store_iterate callback received NULL user_data; "
379 "expected: <pointer to data>");
381 ++(*i);
382 return 0;
383 } /* iter_incr */
385 static int
386 iter_error(sdb_store_base_t *obj, void *user_data)
387 {
388 intptr_t *i = user_data;
390 fail_unless(obj != NULL,
391 "sdb_store_iterate callback received NULL obj; expected: "
392 "<store base obj>");
393 fail_unless(i != NULL,
394 "sdb_store_iterate callback received NULL user_data; "
395 "expected: <pointer to data>");
397 ++(*i);
398 return -1;
399 } /* iter_error */
401 START_TEST(test_iterate)
402 {
403 intptr_t i = 0;
404 int check;
406 /* empty store */
407 check = sdb_store_iterate(iter_incr, &i);
408 fail_unless(check == -1,
409 "sdb_store_iterate(), empty store = %d; expected: -1", check);
410 fail_unless(i == 0,
411 "sdb_store_iterate called callback %d times; expected: 0", (int)i);
413 populate();
415 check = sdb_store_iterate(iter_incr, &i);
416 fail_unless(check == 0,
417 "sdb_store_iterate() = %d; expected: 0", check);
418 fail_unless(i == 2,
419 "sdb_store_iterate called callback %d times; expected: 1", (int)i);
421 i = 0;
422 check = sdb_store_iterate(iter_error, &i);
423 fail_unless(check == -1,
424 "sdb_store_iterate(), error callback = %d; expected: -1", check);
425 fail_unless(i == 1,
426 "sdb_store_iterate called callback %d times "
427 "(callback returned error); expected: 1", (int)i);
428 }
429 END_TEST
431 Suite *
432 core_store_suite(void)
433 {
434 Suite *s = suite_create("core::store");
435 TCase *tc;
437 tc = tcase_create("core");
438 tcase_add_test(tc, test_store_tojson);
439 tcase_add_test(tc, test_store_host);
440 tcase_add_test(tc, test_store_get_host);
441 tcase_add_test(tc, test_store_attr);
442 tcase_add_test(tc, test_store_service);
443 tcase_add_test(tc, test_iterate);
444 tcase_add_unchecked_fixture(tc, NULL, sdb_store_clear);
445 suite_add_tcase(s, tc);
447 return s;
448 } /* core_store_suite */
450 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */