1 /*
2 * SysDB - t/unit/utils/strbuf_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 "utils/strbuf.h"
29 #include "libsysdb_test.h"
31 #include <check.h>
33 /*
34 * private data types
35 */
37 static sdb_strbuf_t *buf;
39 static void
40 setup(void)
41 {
42 buf = sdb_strbuf_create(0);
43 fail_unless(buf != NULL,
44 "sdb_strbuf_create() = NULL; expected strbuf object");
45 } /* setup */
47 static void
48 teardown(void)
49 {
50 sdb_strbuf_destroy(buf);
51 buf = NULL;
52 } /* teardown */
54 /*
55 * tests
56 */
58 START_TEST(test_null)
59 {
60 sdb_strbuf_t *b = NULL;
61 va_list ap;
63 size_t check;
65 /* check that methods don't crash */
66 sdb_strbuf_destroy(b);
67 sdb_strbuf_skip(b, 0, 0);
68 sdb_strbuf_skip(b, 0, 10);
69 sdb_strbuf_skip(b, 10, 10);
70 sdb_strbuf_clear(b);
72 /* check that methods return an error */
73 fail_unless(sdb_strbuf_vappend(b, "test", ap) < 0,
74 "sdb_strbuf_vappend(NULL) didn't report failure");
75 fail_unless(sdb_strbuf_append(b, "test") < 0,
76 "sdb_strbuf_append(NULL) didn't report failure");
77 fail_unless(sdb_strbuf_vsprintf(b, "test", ap) < 0,
78 "sdb_strbuf_vsprintf(NULL) didn't report failure");
79 fail_unless(sdb_strbuf_sprintf(b, "test") < 0,
80 "sdb_strbuf_sprintf(NULL) didn't report failure");
81 fail_unless(sdb_strbuf_memcpy(b, "test", 4) < 0,
82 "sdb_strbuf_memcpy(NULL) didn't report failure");
83 fail_unless(sdb_strbuf_memappend(b, "test", 4) < 0,
84 "sdb_strbuf_memappend(NULL) didn't report failure");
85 fail_unless(sdb_strbuf_read(b, 0, 32) < 0,
86 "sdb_strbuf_read(NULL) didn't report failure");
87 fail_unless(sdb_strbuf_chomp(b) < 0,
88 "sdb_strbuf_chomp(NULL) didn't report failure");
90 /* check that methods return no used space */
91 check = sdb_strbuf_len(b);
92 fail_unless(check == 0,
93 "sdb_strbuf_len(NULL) = %zi; expected: 0", check);
94 check = sdb_strbuf_cap(b);
95 fail_unless(check == 0,
96 "sdb_strbuf_cap(NULL) = %zi; expected: 0", check);
97 }
98 END_TEST
100 START_TEST(test_empty)
101 {
102 sdb_strbuf_t *b = sdb_strbuf_create(0);
103 const char *data;
104 size_t check;
106 /* check that methods don't crash */
107 sdb_strbuf_skip(b, 1, 1);
108 sdb_strbuf_clear(b);
109 sdb_strbuf_chomp(b);
111 data = sdb_strbuf_string(b);
112 fail_unless(data && (*data == '\0'),
113 "sdb_strbuf_string(<empty>) = '%s'; expected: ''", data);
114 check = sdb_strbuf_len(b);
115 fail_unless(check == 0,
116 "sdb_strbuf_len(<empty>) = %zu; expected: 0", check);
117 check = sdb_strbuf_cap(b);
118 fail_unless(check == 0,
119 "sdb_strbuf_cap(<empty>) = %zu; expected: 0", check);
121 sdb_strbuf_destroy(b);
122 }
123 END_TEST
125 START_TEST(test_create)
126 {
127 sdb_strbuf_t *s;
128 size_t check;
130 s = sdb_strbuf_create(0);
131 fail_unless(s != NULL,
132 "sdb_strbuf_create() = NULL; expected strbuf object");
133 check = sdb_strbuf_len(s);
134 fail_unless(check == 0,
135 "sdb_strbuf_create() created buffer with len = %zu; "
136 "expected: 0", check);
137 check = sdb_strbuf_cap(s);
138 fail_unless(check == 0,
139 "sdb_strbuf_create() created buffer with cap = %zu; "
140 "expected: 0", check);
141 sdb_strbuf_destroy(s);
143 s = sdb_strbuf_create(128);
144 fail_unless(s != NULL,
145 "sdb_strbuf_create() = NULL; expected strbuf object");
146 check = sdb_strbuf_len(s);
147 /* len still has to be 0 -- there's no content */
148 fail_unless(check == 0,
149 "sdb_strbuf_create() created buffer with len = %zu; "
150 "expected: 0", check);
151 check = sdb_strbuf_cap(s);
152 fail_unless(check == 128,
153 "sdb_strbuf_create() created buffer with cap = %zu; "
154 "expected: 128", check);
155 sdb_strbuf_destroy(s);
156 }
157 END_TEST
159 START_TEST(test_append)
160 {
161 ssize_t n;
162 size_t len, total = 0;
163 const char *test;
165 struct {
166 const char *input;
167 const char *result;
168 } golden_data[] = {
169 { "1234567890", "1234567890" },
170 { "ABCDE", "1234567890ABCDE" },
171 { "", "1234567890ABCDE" },
172 { "-", "1234567890ABCDE-" },
173 /* when adding anything to this array, the last check has to be
174 * updated accordingly */
175 };
177 size_t i;
179 for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
180 n = sdb_strbuf_append(buf, "%s", golden_data[i].input);
181 fail_unless((size_t)n == strlen(golden_data[i].input),
182 "sdb_strbuf_append() appended %zi bytes; expected: %zu",
183 n, strlen(golden_data[i].input));
184 total += n;
185 len = sdb_strbuf_len(buf);
186 fail_unless(len == total,
187 "sdb_strbuf_append() left behind buffer with len = %zu; "
188 "expected: %zu", len, total);
190 test = sdb_strbuf_string(buf);
191 fail_unless(test[len] == '\0',
192 "sdb_strbuf_append() did not nil-terminate the string");
194 test = sdb_strbuf_string(buf);
195 fail_unless(!strcmp(test, golden_data[i].result),
196 "sdb_strbuf_append() did not correctly concatenate "
197 "the input; got: %s; expected: %s",
198 test, golden_data[i].result);
199 }
201 n = sdb_strbuf_append(buf, "%zu; %5.4f", (size_t)42, 4.2);
202 fail_unless(n == 10,
203 "sdb_strbuf_append() appended %zi bytes; expected: 10", n);
204 total += n;
205 len = sdb_strbuf_len(buf);
206 fail_unless(len == total,
207 "sdb_strbuf_append() left behind buffer with len = %zu; "
208 "expected: %zu", len, total);
210 test = sdb_strbuf_string(buf);
211 fail_unless(test[len] == '\0',
212 "sdb_strbuf_append() did not nil-terminate the string");
213 fail_unless(!strcmp(test, "1234567890ABCDE-42; 4.2000"),
214 "sdb_strbuf_append() did not correctly concatenate the input; "
215 "got: %s; expected: 1234567890ABCDE-42; 4.2000", test);
216 }
217 END_TEST
219 START_TEST(test_sprintf)
220 {
221 ssize_t n;
222 size_t check;
223 const char *test;
225 const char *golden_data[] = {
226 "1234567890",
227 "ABCDE",
228 "",
229 "-",
230 "123456789012345678901234567890",
231 "--",
232 };
234 size_t i;
236 sdb_strbuf_destroy(buf);
237 buf = sdb_strbuf_create(1); /* -> min_size = 1 */
239 for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
240 n = sdb_strbuf_sprintf(buf, "%s", golden_data[i]);
241 fail_unless((size_t)n == strlen(golden_data[i]),
242 "sdb_strbuf_sprintf() wrote %zi bytes; expected: %zu",
243 n, strlen(golden_data[i]));
244 check = sdb_strbuf_len(buf);
245 fail_unless(check == (size_t)n,
246 "sdb_strbuf_sprintf() left behind buffer with len = %zu; "
247 "expected: %zi", check, n);
249 test = sdb_strbuf_string(buf);
250 fail_unless(test[check] == '\0',
251 "sdb_strbuf_sprintf() did not nil-terminate the string");
252 fail_unless(!strcmp(test, golden_data[i]),
253 "sdb_strbuf_sprintf() did not format string correctly; "
254 "got: %s; expected: %s", test, golden_data[i]);
256 check = sdb_strbuf_cap(buf);
257 /* 3 times the current len is the threshold for shrinking,
258 * but it's capped to the initial size (or 64) */
259 if (n)
260 fail_unless(((size_t)n < check) && (check < (size_t)n * 3),
261 "sdb_strbuf_sprintf() did not resize the buffer "
262 "correctly (got size %zu; expected %zi < <size> < %d)",
263 check, n, n / 3);
264 }
266 n = sdb_strbuf_sprintf(buf, "%zu; %5.4f", (size_t)42, 4.2);
267 fail_unless(n == 10,
268 "sdb_strbuf_sprintf() wrote %zi bytes; expected: 10", n);
269 check = sdb_strbuf_len(buf);
270 fail_unless(check == 10,
271 "sdb_strbuf_sprintf() left behind buffer with len = %zu; "
272 "expected: 10", check);
274 test = sdb_strbuf_string(buf);
275 fail_unless(test[check] == '\0',
276 "sdb_strbuf_sprintf() did not nil-terminate the string");
277 fail_unless(!strcmp(test, "42; 4.2000"),
278 "sdb_strbuf_sprintf() did not format string correctly; "
279 "got: %s; expected: 42; 4.2000", test);
280 }
281 END_TEST
283 START_TEST(test_incremental)
284 {
285 const char *data;
287 ssize_t n;
288 size_t i, check;
290 sdb_strbuf_destroy(buf);
291 buf = sdb_strbuf_create(1024);
293 /* fill buffer one by one; leave room for nul-byte */
294 for (i = 0; i < 1023; ++i) {
295 n = sdb_strbuf_append(buf, ".");
296 fail_unless(n == 1, "sdb_strbuf_append() = %zi; expected: 1", n);
297 }
299 check = sdb_strbuf_cap(buf);
300 fail_unless(check == 1024,
301 "sdb_strbuf_append() resizes the buffer too early "
302 "(got size %zu; expected: 1024)", check);
304 /* write another byte; this has to trigger a resize */
305 n = sdb_strbuf_append(buf, ".");
306 fail_unless(n == 1, "sdb_strbuf_append() = %zi; expected: 1", n);
308 check = sdb_strbuf_cap(buf);
309 /* the exact size doesn't matter but is an implementation detail */
310 fail_unless(check > 1024,
311 "sdb_strbuf_append() did not resize the buffer");
313 /* write more bytes; this should trigger at least one more resize but
314 * that's an implementation detail as well */
315 for (i = 0; i < 1024; ++i) {
316 n = sdb_strbuf_append(buf, ".");
317 fail_unless(n == 1, "sdb_strbuf_append() = %zi; expected: 1", n);
318 }
320 n = (ssize_t)sdb_strbuf_len(buf);
321 fail_unless(n == 2048, "sdb_strbuf_len() = %zi; expectd: 2048", n);
323 data = sdb_strbuf_string(buf);
324 for (i = 0; i < 2048; ++i)
325 fail_unless(data[i] == '.',
326 "After sdb_strbuf_append(), found character %x "
327 "at position %zi; expected %x (.)",
328 (int)data[i], i, '.');
329 fail_unless(data[i] == '\0',
330 "After sdb_strbuf_append(), found character %x at end of string; "
331 "expected '\\0'", (int)data[i]);
332 }
333 END_TEST
335 /* used by test_memcpy and test_memappend */
336 static struct {
337 const char *input;
338 size_t size;
339 } mem_golden_data[] = {
340 { "abc\0\x10\x42", 6 },
341 { "\0\1\2\3\4", 5 },
342 { "\n\n\0\n\n", 5 },
343 { "", 0 },
344 };
346 START_TEST(test_memcpy)
347 {
348 size_t i;
350 for (i = 0; i < SDB_STATIC_ARRAY_LEN(mem_golden_data); ++i) {
351 ssize_t n;
352 const char *check;
354 n = sdb_strbuf_memcpy(buf, mem_golden_data[i].input,
355 mem_golden_data[i].size);
356 fail_unless(n >= 0,
357 "sdb_strbuf_memcpy() = %zi; expected: >=0", n);
358 fail_unless((size_t)n == mem_golden_data[i].size,
359 "sdb_strbuf_memcpy() = %zi; expected: %zu",
360 n, mem_golden_data[i].size);
362 n = (ssize_t)sdb_strbuf_len(buf);
363 fail_unless((size_t)n == mem_golden_data[i].size,
364 "sdb_strbuf_len() = %zu (after memcpy); expected: %zu",
365 n, mem_golden_data[i].size);
367 check = sdb_strbuf_string(buf);
368 fail_unless(check != NULL,
369 "sdb_strbuf_string() = NULL (after memcpy); expected: data");
370 fail_unless(check[mem_golden_data[i].size] == '\0',
371 "sdb_strbuf_memcpy() did not nil-terminate the data");
372 fail_unless(!memcmp(check, mem_golden_data[i].input,
373 mem_golden_data[i].size),
374 "sdb_strbuf_memcpy() did not set the buffer correctly");
376 n = (ssize_t)sdb_strbuf_cap(buf);
377 fail_unless((size_t)n > mem_golden_data[i].size,
378 "sdb_strbuf_memcpy() did not resize the buffer correctly "
379 "(got size %zi; expected: > %zu)", n,
380 mem_golden_data[i].size);
381 }
382 }
383 END_TEST
385 START_TEST(test_memappend)
386 {
387 size_t i;
389 for (i = 0; i < SDB_STATIC_ARRAY_LEN(mem_golden_data); ++i) {
390 ssize_t n;
391 const char *check;
393 size_t total, j;
395 n = sdb_strbuf_memappend(buf, mem_golden_data[i].input,
396 mem_golden_data[i].size);
397 fail_unless(n >= 0,
398 "sdb_strbuf_memappend() = %zi; expected: >=0", n);
399 fail_unless((size_t)n == mem_golden_data[i].size,
400 "sdb_strbuf_memappend() = %zi; expected: %zu",
401 n, mem_golden_data[i].size);
403 check = sdb_strbuf_string(buf);
404 fail_unless(check != NULL,
405 "sdb_strbuf_string() = NULL (after memappend); "
406 "expected: data");
408 n = (ssize_t)sdb_strbuf_len(buf);
409 total = 0;
410 for (j = 0; j <= i; ++j) {
411 fail_unless(total + mem_golden_data[j].size <= (size_t)n,
412 "sdb_strbuf_len() = %zu (after memappend); "
413 "expected: >=%zu", n, total + mem_golden_data[j].size);
415 fail_unless(!memcmp(check + total, mem_golden_data[j].input,
416 mem_golden_data[j].size),
417 "sdb_strbuf_memappend() did not "
418 "set the buffer correctly");
419 total += mem_golden_data[j].size;
420 }
421 fail_unless((size_t)n == total,
422 "sdb_strbuf_len() = %zu (after memappend); expected: %zu",
423 n, total);
425 fail_unless(check[total] == '\0',
426 "sdb_strbuf_memappend() did not nil-terminate the data");
428 n = (ssize_t)sdb_strbuf_cap(buf);
429 fail_unless((size_t)n > total,
430 "sdb_strbuf_memappend() did not resize the buffer correctly "
431 "(got size %zi; expected: > %zu)", n, total);
432 }
433 }
434 END_TEST
436 START_TEST(test_chomp)
437 {
438 struct {
439 const char *input;
440 ssize_t expected;
441 const char *expected_string;
442 } golden_data[] = {
443 { NULL, 0, "" },
444 { "\n", 1, "" },
445 { "\n\n", 2, "" },
446 { "12345\n\n\n", 3, "12345" },
447 { "abcd", 0, "abcd" },
448 };
450 size_t i;
452 for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
453 ssize_t n;
454 const char *check;
456 if (golden_data[i].input)
457 sdb_strbuf_sprintf(buf, "%s", golden_data[i].input);
459 /* empty buffer */
460 n = sdb_strbuf_chomp(buf);
461 fail_unless(n == golden_data[i].expected,
462 "sdb_strbuf_chomp() = %zi; expected: %zi", n,
463 golden_data[i].expected);
465 check = sdb_strbuf_string(buf);
466 fail_unless(!strcmp(check, golden_data[i].expected_string),
467 "sdb_strbuf_chomp() did not correctly remove newlines; "
468 "got string '%s'; expected: '%s'", check,
469 golden_data[i].expected_string);
470 }
471 }
472 END_TEST
474 START_TEST(test_skip)
475 {
476 const char *input = "1234567890";
477 struct {
478 size_t offset;
479 size_t n;
480 const char *expected;
481 size_t expected_len;
482 } golden_data[] = {
483 { 0, 0, "1234567890", 10 },
484 { 0, 1, "234567890", 9 },
485 { 0, 2, "34567890", 8 },
486 { 0, 9, "0", 1 },
487 { 0, 10, "", 0 },
488 { 0, 11, "", 0 },
489 { 0, 100, "", 0 },
490 { 1, 0, "1234567890", 10 },
491 { 1, 1, "134567890", 9 },
492 { 1, 2, "14567890", 8 },
493 { 2, 0, "1234567890", 10 },
494 { 2, 1, "124567890", 9 },
495 { 2, 2, "12567890", 8 },
496 { 2, 3, "1267890", 7 },
497 { 2, 4, "127890", 6 },
498 { 2, 5, "12890", 5 },
499 { 2, 6, "1290", 4 },
500 { 2, 7, "120", 3 },
501 { 2, 8, "12", 2 },
502 { 2, 9, "12", 2 },
503 { 2, 10, "12", 2 },
504 { 8, 1, "123456780", 9 },
505 { 8, 2, "12345678", 8 },
506 { 8, 3, "12345678", 8 },
507 { 9, 1, "123456789", 9 },
508 { 9, 2, "123456789", 9 },
509 { 10, 1, "1234567890", 10 },
510 { 10, 2, "1234567890", 10 },
511 };
513 size_t i;
515 for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
516 const char *check;
517 size_t n;
519 sdb_strbuf_sprintf(buf, "%s", input);
520 sdb_strbuf_skip(buf, golden_data[i].offset,
521 golden_data[i].n);
523 n = sdb_strbuf_len(buf);
524 fail_unless(n == golden_data[i].expected_len,
525 "sdb_strbuf_len() = %zu (after skip); expected: %zu",
526 n, golden_data[i].expected_len);
528 check = sdb_strbuf_string(buf);
529 fail_unless(!!check,
530 "sdb_strbuf_string() = NULL (after skip); expected: string");
532 fail_unless(check[n] == '\0',
533 "sdb_strbuf_skip() did not nil-terminate the string");
535 fail_unless(! strcmp(golden_data[i].expected, check),
536 "sdb_strbuf_skip('%s', %zu) did not skip correctly; "
537 "got string '%s'; expected: '%s'", input,
538 golden_data[i].n, check, golden_data[i].expected);
539 }
540 }
541 END_TEST
543 START_TEST(test_clear)
544 {
545 const char *data;
546 size_t len;
548 sdb_strbuf_append(buf, "abc");
549 len = sdb_strbuf_len(buf);
550 fail_unless(len != 0,
551 "sdb_strbuf_len() = %zu; expected: != 0", len);
553 sdb_strbuf_clear(buf);
554 len = sdb_strbuf_len(buf);
555 fail_unless(len == 0,
556 "sdb_strbuf_len() = %zu (after clear); expected: 0", len);
558 data = sdb_strbuf_string(buf);
559 fail_unless(*data == '\0',
560 "sdb_strbuf_string() = '%s' (after clear); expected: ''", data);
561 }
562 END_TEST
564 START_TEST(test_string)
565 {
566 struct {
567 const char *input;
568 const char *expected;
569 } golden_data[] = {
570 { NULL, "" },
571 { "a", "a" },
572 { "abcdef", "abcdef" },
573 };
575 size_t i;
577 for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
578 const char *check;
580 if (golden_data[i].input)
581 sdb_strbuf_sprintf(buf, "%s", golden_data[i].input);
582 check = sdb_strbuf_string(buf);
583 fail_unless(!strcmp(check, golden_data[i].expected),
584 "sdb_strbuf_string() = '%s'; expected: '%s'",
585 check, golden_data[i].expected);
586 }
587 }
588 END_TEST
590 START_TEST(test_len)
591 {
592 struct {
593 const char *input;
594 size_t expected;
595 } golden_data[] = {
596 { NULL, 0 },
597 { "a", 1 },
598 { "12345", 5 },
599 };
601 size_t i;
603 for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
604 size_t check;
606 if (golden_data[i].input)
607 sdb_strbuf_sprintf(buf, "%s", golden_data[i].input);
608 check = sdb_strbuf_len(buf);
609 fail_unless(check == golden_data[i].expected,
610 "sdb_strbuf_len() = %zu; expected: %zu",
611 check, golden_data[i].expected);
612 }
613 }
614 END_TEST
616 Suite *
617 util_strbuf_suite(void)
618 {
619 Suite *s = suite_create("utils::strbuf");
620 TCase *tc;
622 tc = tcase_create("empty");
623 tcase_add_test(tc, test_null);
624 tcase_add_test(tc, test_empty);
625 suite_add_tcase(s, tc);
627 tc = tcase_create("core");
628 tcase_add_checked_fixture(tc, setup, teardown);
629 tcase_add_test(tc, test_create);
630 tcase_add_test(tc, test_append);
631 tcase_add_test(tc, test_sprintf);
632 tcase_add_test(tc, test_incremental);
633 tcase_add_test(tc, test_memcpy);
634 tcase_add_test(tc, test_memappend);
635 tcase_add_test(tc, test_chomp);
636 tcase_add_test(tc, test_skip);
637 tcase_add_test(tc, test_clear);
638 tcase_add_test(tc, test_string);
639 tcase_add_test(tc, test_len);
640 suite_add_tcase(s, tc);
642 return s;
643 } /* util_strbuf_suite */
645 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */