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 #if HAVE_CONFIG_H
29 # include "config.h"
30 #endif
32 #include "utils/strbuf.h"
33 #include "testutils.h"
35 #include <check.h>
37 /*
38 * private data types
39 */
41 static sdb_strbuf_t *buf;
43 static void
44 setup(void)
45 {
46 buf = sdb_strbuf_create(0);
47 fail_unless(buf != NULL,
48 "sdb_strbuf_create() = NULL; expected strbuf object");
49 } /* setup */
51 static void
52 teardown(void)
53 {
54 sdb_strbuf_destroy(buf);
55 buf = NULL;
56 } /* teardown */
58 /*
59 * tests
60 */
62 START_TEST(test_null)
63 {
64 sdb_strbuf_t *b = NULL;
65 va_list ap;
67 size_t check;
69 /* check that methods don't crash */
70 sdb_strbuf_destroy(b);
71 sdb_strbuf_skip(b, 0, 0);
72 sdb_strbuf_skip(b, 0, 10);
73 sdb_strbuf_skip(b, 10, 10);
74 sdb_strbuf_clear(b);
76 /* check that methods return an error */
77 fail_unless(sdb_strbuf_vappend(b, "test", ap) < 0,
78 "sdb_strbuf_vappend(NULL) didn't report failure");
79 fail_unless(sdb_strbuf_append(b, "test") < 0,
80 "sdb_strbuf_append(NULL) didn't report failure");
81 fail_unless(sdb_strbuf_vsprintf(b, "test", ap) < 0,
82 "sdb_strbuf_vsprintf(NULL) didn't report failure");
83 fail_unless(sdb_strbuf_sprintf(b, "test") < 0,
84 "sdb_strbuf_sprintf(NULL) didn't report failure");
85 fail_unless(sdb_strbuf_memcpy(b, "test", 4) < 0,
86 "sdb_strbuf_memcpy(NULL) didn't report failure");
87 fail_unless(sdb_strbuf_memappend(b, "test", 4) < 0,
88 "sdb_strbuf_memappend(NULL) didn't report failure");
89 fail_unless(sdb_strbuf_read(b, 0, 32) < 0,
90 "sdb_strbuf_read(NULL) didn't report failure");
91 fail_unless(sdb_strbuf_chomp(b) < 0,
92 "sdb_strbuf_chomp(NULL) didn't report failure");
94 /* check that methods return no used space */
95 check = sdb_strbuf_len(b);
96 fail_unless(check == 0,
97 "sdb_strbuf_len(NULL) = %zi; expected: 0", check);
98 check = sdb_strbuf_cap(b);
99 fail_unless(check == 0,
100 "sdb_strbuf_cap(NULL) = %zi; expected: 0", check);
101 }
102 END_TEST
104 START_TEST(test_empty)
105 {
106 sdb_strbuf_t *b = sdb_strbuf_create(0);
107 const char *data;
108 size_t check;
110 /* check that methods don't crash */
111 sdb_strbuf_skip(b, 1, 1);
112 sdb_strbuf_clear(b);
113 sdb_strbuf_chomp(b);
115 data = sdb_strbuf_string(b);
116 fail_unless(data && (*data == '\0'),
117 "sdb_strbuf_string(<empty>) = '%s'; expected: ''", data);
118 check = sdb_strbuf_len(b);
119 fail_unless(check == 0,
120 "sdb_strbuf_len(<empty>) = %zu; expected: 0", check);
121 check = sdb_strbuf_cap(b);
122 fail_unless(check == 0,
123 "sdb_strbuf_cap(<empty>) = %zu; expected: 0", check);
125 sdb_strbuf_destroy(b);
126 }
127 END_TEST
129 START_TEST(test_create)
130 {
131 sdb_strbuf_t *s;
132 size_t check;
134 s = sdb_strbuf_create(0);
135 fail_unless(s != NULL,
136 "sdb_strbuf_create() = NULL; expected strbuf object");
137 check = sdb_strbuf_len(s);
138 fail_unless(check == 0,
139 "sdb_strbuf_create() created buffer with len = %zu; "
140 "expected: 0", check);
141 check = sdb_strbuf_cap(s);
142 fail_unless(check == 0,
143 "sdb_strbuf_create() created buffer with cap = %zu; "
144 "expected: 0", check);
145 sdb_strbuf_destroy(s);
147 s = sdb_strbuf_create(128);
148 fail_unless(s != NULL,
149 "sdb_strbuf_create() = NULL; expected strbuf object");
150 check = sdb_strbuf_len(s);
151 /* len still has to be 0 -- there's no content */
152 fail_unless(check == 0,
153 "sdb_strbuf_create() created buffer with len = %zu; "
154 "expected: 0", check);
155 check = sdb_strbuf_cap(s);
156 fail_unless(check == 128,
157 "sdb_strbuf_create() created buffer with cap = %zu; "
158 "expected: 128", check);
159 sdb_strbuf_destroy(s);
160 }
161 END_TEST
163 START_TEST(test_append)
164 {
165 ssize_t n;
166 size_t len, total = 0;
167 const char *test;
169 struct {
170 const char *input;
171 const char *result;
172 } golden_data[] = {
173 { "1234567890", "1234567890" },
174 { "ABCDE", "1234567890ABCDE" },
175 { "", "1234567890ABCDE" },
176 { "-", "1234567890ABCDE-" },
177 /* when adding anything to this array, the last check has to be
178 * updated accordingly */
179 };
181 size_t i;
183 for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
184 n = sdb_strbuf_append(buf, "%s", golden_data[i].input);
185 fail_unless((size_t)n == strlen(golden_data[i].input),
186 "sdb_strbuf_append() appended %zi bytes; expected: %zu",
187 n, strlen(golden_data[i].input));
188 total += n;
189 len = sdb_strbuf_len(buf);
190 fail_unless(len == total,
191 "sdb_strbuf_append() left behind buffer with len = %zu; "
192 "expected: %zu", len, total);
194 test = sdb_strbuf_string(buf);
195 fail_unless(test[len] == '\0',
196 "sdb_strbuf_append() did not nil-terminate the string");
198 test = sdb_strbuf_string(buf);
199 fail_unless(!strcmp(test, golden_data[i].result),
200 "sdb_strbuf_append() did not correctly concatenate "
201 "the input; got: %s; expected: %s",
202 test, golden_data[i].result);
203 }
205 n = sdb_strbuf_append(buf, "%zu; %5.4f", (size_t)42, 4.2);
206 fail_unless(n == 10,
207 "sdb_strbuf_append() appended %zi bytes; expected: 10", n);
208 total += n;
209 len = sdb_strbuf_len(buf);
210 fail_unless(len == total,
211 "sdb_strbuf_append() left behind buffer with len = %zu; "
212 "expected: %zu", len, total);
214 test = sdb_strbuf_string(buf);
215 fail_unless(test[len] == '\0',
216 "sdb_strbuf_append() did not nil-terminate the string");
217 fail_unless(!strcmp(test, "1234567890ABCDE-42; 4.2000"),
218 "sdb_strbuf_append() did not correctly concatenate the input; "
219 "got: %s; expected: 1234567890ABCDE-42; 4.2000", test);
220 }
221 END_TEST
223 START_TEST(test_sprintf)
224 {
225 ssize_t n;
226 size_t check;
227 const char *test;
229 const char *golden_data[] = {
230 "1234567890",
231 "ABCDE",
232 "",
233 "-",
234 "123456789012345678901234567890",
235 "--",
236 };
238 size_t i;
240 sdb_strbuf_destroy(buf);
241 buf = sdb_strbuf_create(1); /* -> min_size = 1 */
243 for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
244 n = sdb_strbuf_sprintf(buf, "%s", golden_data[i]);
245 fail_unless((size_t)n == strlen(golden_data[i]),
246 "sdb_strbuf_sprintf() wrote %zi bytes; expected: %zu",
247 n, strlen(golden_data[i]));
248 check = sdb_strbuf_len(buf);
249 fail_unless(check == (size_t)n,
250 "sdb_strbuf_sprintf() left behind buffer with len = %zu; "
251 "expected: %zi", check, n);
253 test = sdb_strbuf_string(buf);
254 fail_unless(test[check] == '\0',
255 "sdb_strbuf_sprintf() did not nil-terminate the string");
256 fail_unless(!strcmp(test, golden_data[i]),
257 "sdb_strbuf_sprintf() did not format string correctly; "
258 "got: %s; expected: %s", test, golden_data[i]);
260 check = sdb_strbuf_cap(buf);
261 /* 3 times the current len is the threshold for shrinking,
262 * but it's capped to the initial size (or 64) */
263 if (n)
264 fail_unless(((size_t)n < check) && (check < (size_t)n * 3),
265 "sdb_strbuf_sprintf() did not resize the buffer "
266 "correctly (got size %zu; expected %zi < <size> < %d)",
267 check, n, n / 3);
268 }
270 n = sdb_strbuf_sprintf(buf, "%zu; %5.4f", (size_t)42, 4.2);
271 fail_unless(n == 10,
272 "sdb_strbuf_sprintf() wrote %zi bytes; expected: 10", n);
273 check = sdb_strbuf_len(buf);
274 fail_unless(check == 10,
275 "sdb_strbuf_sprintf() left behind buffer with len = %zu; "
276 "expected: 10", check);
278 test = sdb_strbuf_string(buf);
279 fail_unless(test[check] == '\0',
280 "sdb_strbuf_sprintf() did not nil-terminate the string");
281 fail_unless(!strcmp(test, "42; 4.2000"),
282 "sdb_strbuf_sprintf() did not format string correctly; "
283 "got: %s; expected: 42; 4.2000", test);
284 }
285 END_TEST
287 START_TEST(test_incremental)
288 {
289 const char *data;
291 ssize_t n;
292 size_t i, check;
294 sdb_strbuf_destroy(buf);
295 buf = sdb_strbuf_create(1024);
297 /* fill buffer one by one; leave room for nul-byte */
298 for (i = 0; i < 1023; ++i) {
299 n = sdb_strbuf_append(buf, ".");
300 fail_unless(n == 1, "sdb_strbuf_append() = %zi; expected: 1", n);
301 }
303 check = sdb_strbuf_cap(buf);
304 fail_unless(check == 1024,
305 "sdb_strbuf_append() resizes the buffer too early "
306 "(got size %zu; expected: 1024)", check);
308 /* write another byte; this has to trigger a resize */
309 n = sdb_strbuf_append(buf, ".");
310 fail_unless(n == 1, "sdb_strbuf_append() = %zi; expected: 1", n);
312 check = sdb_strbuf_cap(buf);
313 /* the exact size doesn't matter but is an implementation detail */
314 fail_unless(check > 1024,
315 "sdb_strbuf_append() did not resize the buffer");
317 /* write more bytes; this should trigger at least one more resize but
318 * that's an implementation detail as well */
319 for (i = 0; i < 1024; ++i) {
320 n = sdb_strbuf_append(buf, ".");
321 fail_unless(n == 1, "sdb_strbuf_append() = %zi; expected: 1", n);
322 }
324 n = (ssize_t)sdb_strbuf_len(buf);
325 fail_unless(n == 2048, "sdb_strbuf_len() = %zi; expectd: 2048", n);
327 data = sdb_strbuf_string(buf);
328 for (i = 0; i < 2048; ++i)
329 fail_unless(data[i] == '.',
330 "After sdb_strbuf_append(), found character %x "
331 "at position %zi; expected %x (.)",
332 (int)data[i], i, '.');
333 fail_unless(data[i] == '\0',
334 "After sdb_strbuf_append(), found character %x at end of string; "
335 "expected '\\0'", (int)data[i]);
336 }
337 END_TEST
339 /* used by test_memcpy and test_memappend */
340 static struct {
341 const char *input;
342 size_t size;
343 } mem_golden_data[] = {
344 { "abc\0\x10\x42", 6 },
345 { "\0\1\2\3\4", 5 },
346 { "\n\n\0\n\n", 5 },
347 { "", 0 },
348 };
350 START_TEST(test_memcpy)
351 {
352 size_t i;
354 for (i = 0; i < SDB_STATIC_ARRAY_LEN(mem_golden_data); ++i) {
355 ssize_t n;
356 const char *check;
358 n = sdb_strbuf_memcpy(buf, mem_golden_data[i].input,
359 mem_golden_data[i].size);
360 fail_unless(n >= 0,
361 "sdb_strbuf_memcpy() = %zi; expected: >=0", n);
362 fail_unless((size_t)n == mem_golden_data[i].size,
363 "sdb_strbuf_memcpy() = %zi; expected: %zu",
364 n, mem_golden_data[i].size);
366 n = (ssize_t)sdb_strbuf_len(buf);
367 fail_unless((size_t)n == mem_golden_data[i].size,
368 "sdb_strbuf_len() = %zu (after memcpy); expected: %zu",
369 n, mem_golden_data[i].size);
371 check = sdb_strbuf_string(buf);
372 fail_unless(check != NULL,
373 "sdb_strbuf_string() = NULL (after memcpy); expected: data");
374 fail_unless(check[mem_golden_data[i].size] == '\0',
375 "sdb_strbuf_memcpy() did not nil-terminate the data");
376 fail_unless(!memcmp(check, mem_golden_data[i].input,
377 mem_golden_data[i].size),
378 "sdb_strbuf_memcpy() did not set the buffer correctly");
380 n = (ssize_t)sdb_strbuf_cap(buf);
381 fail_unless((size_t)n > mem_golden_data[i].size,
382 "sdb_strbuf_memcpy() did not resize the buffer correctly "
383 "(got size %zi; expected: > %zu)", n,
384 mem_golden_data[i].size);
385 }
386 }
387 END_TEST
389 START_TEST(test_memappend)
390 {
391 size_t i;
393 for (i = 0; i < SDB_STATIC_ARRAY_LEN(mem_golden_data); ++i) {
394 ssize_t n;
395 const char *check;
397 size_t total, j;
399 n = sdb_strbuf_memappend(buf, mem_golden_data[i].input,
400 mem_golden_data[i].size);
401 fail_unless(n >= 0,
402 "sdb_strbuf_memappend() = %zi; expected: >=0", n);
403 fail_unless((size_t)n == mem_golden_data[i].size,
404 "sdb_strbuf_memappend() = %zi; expected: %zu",
405 n, mem_golden_data[i].size);
407 check = sdb_strbuf_string(buf);
408 fail_unless(check != NULL,
409 "sdb_strbuf_string() = NULL (after memappend); "
410 "expected: data");
412 n = (ssize_t)sdb_strbuf_len(buf);
413 total = 0;
414 for (j = 0; j <= i; ++j) {
415 fail_unless(total + mem_golden_data[j].size <= (size_t)n,
416 "sdb_strbuf_len() = %zu (after memappend); "
417 "expected: >=%zu", n, total + mem_golden_data[j].size);
419 fail_unless(!memcmp(check + total, mem_golden_data[j].input,
420 mem_golden_data[j].size),
421 "sdb_strbuf_memappend() did not "
422 "set the buffer correctly");
423 total += mem_golden_data[j].size;
424 }
425 fail_unless((size_t)n == total,
426 "sdb_strbuf_len() = %zu (after memappend); expected: %zu",
427 n, total);
429 fail_unless(check[total] == '\0',
430 "sdb_strbuf_memappend() did not nil-terminate the data");
432 n = (ssize_t)sdb_strbuf_cap(buf);
433 fail_unless((size_t)n > total,
434 "sdb_strbuf_memappend() did not resize the buffer correctly "
435 "(got size %zi; expected: > %zu)", n, total);
436 }
437 }
438 END_TEST
440 START_TEST(test_chomp)
441 {
442 struct {
443 const char *input;
444 ssize_t expected;
445 const char *expected_string;
446 } golden_data[] = {
447 { NULL, 0, "" },
448 { "\n", 1, "" },
449 { "\n\n", 2, "" },
450 { "12345\n\n\n", 3, "12345" },
451 { "abcd", 0, "abcd" },
452 };
454 size_t i;
456 for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
457 ssize_t n;
458 const char *check;
460 if (golden_data[i].input)
461 sdb_strbuf_sprintf(buf, "%s", golden_data[i].input);
463 /* empty buffer */
464 n = sdb_strbuf_chomp(buf);
465 fail_unless(n == golden_data[i].expected,
466 "sdb_strbuf_chomp() = %zi; expected: %zi", n,
467 golden_data[i].expected);
469 check = sdb_strbuf_string(buf);
470 fail_unless(!strcmp(check, golden_data[i].expected_string),
471 "sdb_strbuf_chomp() did not correctly remove newlines; "
472 "got string '%s'; expected: '%s'", check,
473 golden_data[i].expected_string);
474 }
475 }
476 END_TEST
478 START_TEST(test_skip)
479 {
480 const char *input = "1234567890";
481 struct {
482 size_t offset;
483 size_t n;
484 const char *expected;
485 size_t expected_len;
486 } golden_data[] = {
487 { 0, 0, "1234567890", 10 },
488 { 0, 1, "234567890", 9 },
489 { 0, 2, "34567890", 8 },
490 { 0, 9, "0", 1 },
491 { 0, 10, "", 0 },
492 { 0, 11, "", 0 },
493 { 0, 100, "", 0 },
494 { 1, 0, "1234567890", 10 },
495 { 1, 1, "134567890", 9 },
496 { 1, 2, "14567890", 8 },
497 { 2, 0, "1234567890", 10 },
498 { 2, 1, "124567890", 9 },
499 { 2, 2, "12567890", 8 },
500 { 2, 3, "1267890", 7 },
501 { 2, 4, "127890", 6 },
502 { 2, 5, "12890", 5 },
503 { 2, 6, "1290", 4 },
504 { 2, 7, "120", 3 },
505 { 2, 8, "12", 2 },
506 { 2, 9, "12", 2 },
507 { 2, 10, "12", 2 },
508 { 8, 1, "123456780", 9 },
509 { 8, 2, "12345678", 8 },
510 { 8, 3, "12345678", 8 },
511 { 9, 1, "123456789", 9 },
512 { 9, 2, "123456789", 9 },
513 { 10, 1, "1234567890", 10 },
514 { 10, 2, "1234567890", 10 },
515 };
517 size_t i;
519 for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
520 const char *check;
521 size_t n;
523 sdb_strbuf_sprintf(buf, "%s", input);
524 sdb_strbuf_skip(buf, golden_data[i].offset,
525 golden_data[i].n);
527 n = sdb_strbuf_len(buf);
528 fail_unless(n == golden_data[i].expected_len,
529 "sdb_strbuf_len() = %zu (after skip); expected: %zu",
530 n, golden_data[i].expected_len);
532 check = sdb_strbuf_string(buf);
533 fail_unless(!!check,
534 "sdb_strbuf_string() = NULL (after skip); expected: string");
536 fail_unless(check[n] == '\0',
537 "sdb_strbuf_skip() did not nil-terminate the string");
539 fail_unless(! strcmp(golden_data[i].expected, check),
540 "sdb_strbuf_skip('%s', %zu) did not skip correctly; "
541 "got string '%s'; expected: '%s'", input,
542 golden_data[i].n, check, golden_data[i].expected);
543 }
544 }
545 END_TEST
547 START_TEST(test_clear)
548 {
549 const char *data;
550 size_t len;
552 sdb_strbuf_append(buf, "abc");
553 len = sdb_strbuf_len(buf);
554 fail_unless(len != 0,
555 "sdb_strbuf_len() = %zu; expected: != 0", len);
557 sdb_strbuf_clear(buf);
558 len = sdb_strbuf_len(buf);
559 fail_unless(len == 0,
560 "sdb_strbuf_len() = %zu (after clear); expected: 0", len);
562 data = sdb_strbuf_string(buf);
563 fail_unless(*data == '\0',
564 "sdb_strbuf_string() = '%s' (after clear); expected: ''", data);
565 }
566 END_TEST
568 START_TEST(test_string)
569 {
570 struct {
571 const char *input;
572 const char *expected;
573 } golden_data[] = {
574 { NULL, "" },
575 { "a", "a" },
576 { "abcdef", "abcdef" },
577 };
579 size_t i;
581 for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
582 const char *check;
584 if (golden_data[i].input)
585 sdb_strbuf_sprintf(buf, "%s", golden_data[i].input);
586 check = sdb_strbuf_string(buf);
587 fail_unless(!strcmp(check, golden_data[i].expected),
588 "sdb_strbuf_string() = '%s'; expected: '%s'",
589 check, golden_data[i].expected);
590 }
591 }
592 END_TEST
594 START_TEST(test_len)
595 {
596 struct {
597 const char *input;
598 size_t expected;
599 } golden_data[] = {
600 { NULL, 0 },
601 { "a", 1 },
602 { "12345", 5 },
603 };
605 size_t i;
607 for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
608 size_t check;
610 if (golden_data[i].input)
611 sdb_strbuf_sprintf(buf, "%s", golden_data[i].input);
612 check = sdb_strbuf_len(buf);
613 fail_unless(check == golden_data[i].expected,
614 "sdb_strbuf_len() = %zu; expected: %zu",
615 check, golden_data[i].expected);
616 }
617 }
618 END_TEST
620 TEST_MAIN("utils::strbuf")
621 {
622 TCase *tc = tcase_create("empty");
623 tcase_add_test(tc, test_null);
624 tcase_add_test(tc, test_empty);
625 ADD_TCASE(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 ADD_TCASE(tc);
641 }
642 TEST_MAIN_END
644 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */