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 "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 }
102 }
103 END_TEST
105 START_TEST(test_store_get_host)
106 {
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_base_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_base_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 }
163 }
164 END_TEST
166 START_TEST(test_store_attr)
167 {
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 }
205 }
206 END_TEST
208 START_TEST(test_store_service)
209 {
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 }
240 }
241 END_TEST
243 static void
244 verify_json_output(sdb_strbuf_t *buf, const char *expected, int flags)
245 {
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)
271 {
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);
367 }
368 END_TEST
370 START_TEST(test_interval)
371 {
372 sdb_store_base_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));
423 }
424 END_TEST
426 static int
427 iter_incr(sdb_store_base_t *obj, void *user_data)
428 {
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_base_t *obj, void *user_data)
444 {
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)
459 {
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);
485 }
486 END_TEST
488 Suite *
489 core_store_suite(void)
490 {
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 : */