#include "cx/test.h"
#include "util_allocator.h"
#include "cx/printf.h"
#include "cx/buffer.h"
#define ASSERT_ZERO_TERMINATED(str)
CX_TEST_ASSERTM((str).ptr[(str).length] ==
'\0', \
#str " is not zero terminated")
static size_t test_printf_write_func(
const void *src,
size_t esize,
size_t ecount,
void *target
) {
memcpy(target, src, esize * ecount);
return esize * ecount;
}
CX_TEST(test_bprintf) {
CxTestingAllocator talloc;
cx_testing_allocator_init(&talloc);
CxAllocator *alloc = &talloc.base;
CX_TEST_DO {
CxBuffer buf;
cxBufferInit(&buf,
NULL,
64, alloc,
0);
size_t r = cx_bprintf(&buf,
"This %s aged %u years in a %2XSK.",
"Test",
10,
0xca);
CX_TEST_ASSERT(r ==
34);
CX_TEST_ASSERT(buf.size ==
34);
buf.space[r] =
'\0';
CX_TEST_ASSERT(
0 == strcmp(buf.space,
"This Test aged 10 years in a CASK."));
cxBufferDestroy(&buf);
CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
}
cx_testing_allocator_destroy(&talloc);
}
CX_TEST(test_bprintf_large_string) {
unsigned len = cx_printf_sbo_size;
CxTestingAllocator talloc;
cx_testing_allocator_init(&talloc);
CxAllocator *alloc = &talloc.base;
char *aaa = malloc(len);
char *bbb = malloc(len);
char *expected = malloc(
2*len
+16);
memset(aaa,
'a', len
-1);
aaa[len
-1] =
0;
memset(bbb,
'b', len
-1);
bbb[len
-1] =
0;
sprintf(expected,
"After %s comes %s.", aaa, bbb);
CX_TEST_DO {
CxBuffer buf;
cxBufferInit(&buf,
NULL,
64, alloc,
CX_BUFFER_AUTO_EXTEND);
size_t r = cx_bprintf(&buf,
"After %s comes %s.", aaa, bbb);
size_t er =
2*len
-2+14;
CX_TEST_ASSERT(r == er);
CX_TEST_ASSERT(buf.size == er);
cxBufferPut(&buf,
0);
CX_TEST_ASSERT(
0 == strcmp(expected, buf.space));
cxBufferDestroy(&buf);
CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
}
free(aaa);
free(bbb);
free(expected);
cx_testing_allocator_destroy(&talloc);
}
CX_TEST(test_bprintf_nocap) {
CxTestingAllocator talloc;
cx_testing_allocator_init(&talloc);
CxAllocator *alloc = &talloc.base;
char space[
20];
memset(space,
'a',
20);
CX_TEST_DO {
CxBuffer buf;
cxBufferInit(&buf, space,
16, alloc,
0);
size_t r = cx_bprintf(&buf,
"Hello %s with more than %d chars.",
"string",
16);
CX_TEST_ASSERT(r ==
16);
CX_TEST_ASSERT(buf.size ==
16);
CX_TEST_ASSERT(
0 == memcmp(space,
"Hello string witaaaa",
20));
cxBufferDestroy(&buf);
CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
}
cx_testing_allocator_destroy(&talloc);
}
CX_TEST(test_fprintf) {
const char *h =
"Hello";
char buf[
32];
size_t r;
CX_TEST_DO {
r = cx_fprintf(buf, test_printf_write_func,
"teststring");
CX_TEST_ASSERT(r ==
10);
CX_TEST_ASSERT(
0 == memcmp(buf,
"teststring", r));
r = cx_fprintf(buf, test_printf_write_func,
"[%10s]", h);
CX_TEST_ASSERT(r ==
12);
CX_TEST_ASSERT(
0 == memcmp(buf,
"[ Hello]", r));
r = cx_fprintf(buf, test_printf_write_func,
"[%-10s]", h);
CX_TEST_ASSERT(r ==
12);
CX_TEST_ASSERT(
0 == memcmp(buf,
"[Hello ]", r));
r = cx_fprintf(buf, test_printf_write_func,
"[%*s]",
10, h);
CX_TEST_ASSERT(r ==
12);
CX_TEST_ASSERT(
0 == memcmp(buf,
"[ Hello]", r));
r = cx_fprintf(buf, test_printf_write_func,
"[%-10.*s]",
4, h);
CX_TEST_ASSERT(r ==
12);
CX_TEST_ASSERT(
0 == memcmp(buf,
"[Hell ]", r));
r = cx_fprintf(buf, test_printf_write_func,
"[%-*.*s]",
10,
4, h);
CX_TEST_ASSERT(r ==
12);
CX_TEST_ASSERT(
0 == memcmp(buf,
"[Hell ]", r));
r = cx_fprintf(buf, test_printf_write_func,
"%c",
'A');
CX_TEST_ASSERT(r ==
1);
CX_TEST_ASSERT(
0 == memcmp(buf,
"A", r));
r = cx_fprintf(buf, test_printf_write_func,
"%i %d %.6i %i %.0i %+i %i",
1,
2,
3,
0,
0,
4,
-4);
CX_TEST_ASSERT(r ==
19);
CX_TEST_ASSERT(
0 == memcmp(buf,
"1 2 000003 0 +4 -4", r));
r = cx_fprintf(buf, test_printf_write_func,
"%x %x %X %#x",
5,
10,
10,
6);
CX_TEST_ASSERT(r ==
9);
CX_TEST_ASSERT(
0 == memcmp(buf,
"5 a A 0x6", r));
r = cx_fprintf(buf, test_printf_write_func,
"%o %#o %#o",
10,
10,
4);
CX_TEST_ASSERT(r ==
9);
CX_TEST_ASSERT(
0 == memcmp(buf,
"12 012 04", r));
r = cx_fprintf(buf, test_printf_write_func,
"%05.2f %.2f %5.2f",
1.5,
1.5,
1.5);
CX_TEST_ASSERT(r ==
16);
CX_TEST_ASSERT(
0 == memcmp(buf,
"01.50 1.50 1.50", r));
r = cx_fprintf(buf, test_printf_write_func,
"''%*c''",
5,
'x');
CX_TEST_ASSERT(r ==
7);
CX_TEST_ASSERT(
0 == memcmp(buf,
"'' x''", r));
r = cx_fprintf(buf, test_printf_write_func,
"''%*c''",
-5,
'x');
CX_TEST_ASSERT(r ==
7);
CX_TEST_ASSERT(
0 == memcmp(buf,
"''x ''", r));
}
}
CX_TEST(test_asprintf) {
CxTestingAllocator talloc;
cx_testing_allocator_init(&talloc);
CxAllocator *alloc = &talloc.base;
const char *h =
"Hello";
cxmutstr r[
13];
size_t specimen_count = cx_nmemb(r);
size_t specimen =
0;
CX_TEST_DO {
r[specimen] = cx_asprintf_a(alloc,
"teststring");
CX_TEST_ASSERT(r[specimen].length ==
10);
ASSERT_ZERO_TERMINATED(r[specimen]);
CX_TEST_ASSERT(
0 == strcmp(r[specimen].ptr,
"teststring"));
specimen++;
r[specimen] = cx_asprintf_a(alloc,
"[%10s]", h);
CX_TEST_ASSERT(r[specimen].length ==
12);
ASSERT_ZERO_TERMINATED(r[specimen]);
CX_TEST_ASSERT(
0 == strcmp(r[specimen].ptr,
"[ Hello]"));
specimen++;
r[specimen] = cx_asprintf_a(alloc,
"[%-10s]", h);
CX_TEST_ASSERT(r[specimen].length ==
12);
ASSERT_ZERO_TERMINATED(r[specimen]);
CX_TEST_ASSERT(
0 == strcmp(r[specimen].ptr,
"[Hello ]"));
specimen++;
r[specimen] = cx_asprintf_a(alloc,
"[%*s]",
10, h);
CX_TEST_ASSERT(r[specimen].length ==
12);
ASSERT_ZERO_TERMINATED(r[specimen]);
CX_TEST_ASSERT(
0 == strcmp(r[specimen].ptr,
"[ Hello]"));
specimen++;
r[specimen] = cx_asprintf_a(alloc,
"[%-10.*s]",
4, h);
CX_TEST_ASSERT(r[specimen].length ==
12);
ASSERT_ZERO_TERMINATED(r[specimen]);
CX_TEST_ASSERT(
0 == strcmp(r[specimen].ptr,
"[Hell ]"));
specimen++;
r[specimen] = cx_asprintf_a(alloc,
"[%-*.*s]",
10,
4, h);
CX_TEST_ASSERT(r[specimen].length ==
12);
ASSERT_ZERO_TERMINATED(r[specimen]);
CX_TEST_ASSERT(
0 == strcmp(r[specimen].ptr,
"[Hell ]"));
specimen++;
r[specimen] = cx_asprintf_a(alloc,
"%c",
'A');
CX_TEST_ASSERT(r[specimen].length ==
1);
ASSERT_ZERO_TERMINATED(r[specimen]);
CX_TEST_ASSERT(
0 == strcmp(r[specimen].ptr,
"A"));
specimen++;
r[specimen] = cx_asprintf_a(alloc,
"%i %d %.6i %i %.0i %+i %i",
1,
2,
3,
0,
0,
4,
-4);
CX_TEST_ASSERT(r[specimen].length ==
19);
ASSERT_ZERO_TERMINATED(r[specimen]);
CX_TEST_ASSERT(
0 == strcmp(r[specimen].ptr,
"1 2 000003 0 +4 -4"));
specimen++;
r[specimen] = cx_asprintf_a(alloc,
"%x %x %X %#x",
5,
10,
10,
6);
CX_TEST_ASSERT(r[specimen].length ==
9);
ASSERT_ZERO_TERMINATED(r[specimen]);
CX_TEST_ASSERT(
0 == strcmp(r[specimen].ptr,
"5 a A 0x6"));
specimen++;
r[specimen] = cx_asprintf_a(alloc,
"%o %#o %#o",
10,
10,
4);
CX_TEST_ASSERT(r[specimen].length ==
9);
ASSERT_ZERO_TERMINATED(r[specimen]);
CX_TEST_ASSERT(
0 == strcmp(r[specimen].ptr,
"12 012 04"));
specimen++;
r[specimen] = cx_asprintf_a(alloc,
"%05.2f %.2f %5.2f",
1.5,
1.5,
1.5);
CX_TEST_ASSERT(r[specimen].length ==
16);
ASSERT_ZERO_TERMINATED(r[specimen]);
CX_TEST_ASSERT(
0 == strcmp(r[specimen].ptr,
"01.50 1.50 1.50"));
specimen++;
r[specimen] = cx_asprintf_a(alloc,
"''%*c''",
5,
'x');
CX_TEST_ASSERT(r[specimen].length ==
7);
ASSERT_ZERO_TERMINATED(r[specimen]);
CX_TEST_ASSERT(
0 == strcmp(r[specimen].ptr,
"'' x''"));
specimen++;
r[specimen] = cx_asprintf_a(alloc,
"''%*c''",
-5,
'x');
CX_TEST_ASSERT(r[specimen].length ==
7);
ASSERT_ZERO_TERMINATED(r[specimen]);
CX_TEST_ASSERT(
0 == strcmp(r[specimen].ptr,
"''x ''"));
specimen++;
CX_TEST_ASSERT(specimen == specimen_count);
for (
size_t i =
0; i < specimen_count; i++) {
cx_strfree_a(alloc, &r[i]);
}
CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
}
cx_testing_allocator_destroy(&talloc);
}
CX_TEST(test_asprintf_large_string) {
unsigned len = cx_printf_sbo_size;
char *aaa = malloc(len);
char *bbb = malloc(len);
char *expected = malloc(
2*len
+16);
memset(aaa,
'a', len
-1);
aaa[len
-1] =
0;
memset(bbb,
'b', len
-1);
bbb[len
-1] =
0;
sprintf(expected,
"After %s comes %s.", aaa, bbb);
CX_TEST_DO {
cxmutstr r = cx_asprintf(
"After %s comes %s.", aaa, bbb);
CX_TEST_ASSERT(r.length ==
2*len
-2+14);
ASSERT_ZERO_TERMINATED(r);
CX_TEST_ASSERT(
0 == strcmp(r.ptr, expected));
cx_strfree(&r);
}
free(aaa);
free(bbb);
free(expected);
}
CX_TEST(test_sprintf_no_realloc) {
char *buf = malloc(
16);
CxTestingAllocator talloc;
cx_testing_allocator_init(&talloc);
CxAllocator *alloc = &talloc.base;
CX_TEST_DO {
char *oldbuf = buf;
size_t buflen =
16;
size_t len = cx_sprintf_a(alloc, &buf, &buflen,
"Test %d %s",
47,
"string");
CX_TEST_ASSERT(oldbuf == buf);
CX_TEST_ASSERT(len ==
14);
CX_TEST_ASSERT(buflen ==
16);
CX_TEST_ASSERT(
0 == memcmp(buf,
"Test 47 string",
15));
CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
}
cx_testing_allocator_destroy(&talloc);
free(buf);
}
CX_TEST(test_sprintf_realloc) {
CxTestingAllocator talloc;
cx_testing_allocator_init(&talloc);
CxAllocator *alloc = &talloc.base;
char *buf = cxMalloc(alloc,
8);
CX_TEST_DO {
size_t buflen =
8;
size_t len = cx_sprintf_a(alloc, &buf, &buflen,
"Test %d %s",
47,
"foobar");
CX_TEST_ASSERT(len ==
14);
CX_TEST_ASSERT(buflen ==
15);
CX_TEST_ASSERT(
0 == memcmp(buf,
"Test 47 foobar",
15));
cxFree(alloc, buf);
CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
}
cx_testing_allocator_destroy(&talloc);
}
CX_TEST(test_sprintf_realloc_to_fit_terminator) {
CxTestingAllocator talloc;
cx_testing_allocator_init(&talloc);
CxAllocator *alloc = &talloc.base;
char *buf = cxMalloc(alloc,
14);
CX_TEST_DO {
size_t buflen =
14;
size_t len = cx_sprintf_a(alloc, &buf, &buflen,
"Test %d %s",
13,
"string");
CX_TEST_ASSERT(len ==
14);
CX_TEST_ASSERT(buflen ==
15);
CX_TEST_ASSERT(
0 == memcmp(buf,
"Test 13 string",
15));
cxFree(alloc, buf);
CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
}
cx_testing_allocator_destroy(&talloc);
}
CX_TEST(test_sprintf_s_no_alloc) {
char buf[
16];
CxTestingAllocator talloc;
cx_testing_allocator_init(&talloc);
CxAllocator *alloc = &talloc.base;
CX_TEST_DO {
char *str;
size_t buflen =
16;
size_t len = cx_sprintf_sa(alloc, buf, &buflen, &str,
"Test %d %s",
47,
"string");
CX_TEST_ASSERT(str == buf);
CX_TEST_ASSERT(buflen ==
16);
CX_TEST_ASSERT(len ==
14);
CX_TEST_ASSERT(
0 == memcmp(buf,
"Test 47 string",
15));
CX_TEST_ASSERT(
0 == memcmp(str,
"Test 47 string",
15));
CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
}
cx_testing_allocator_destroy(&talloc);
}
CX_TEST(test_sprintf_s_alloc) {
char buf[
16];
memcpy(buf,
"0123456789abcdef",
16);
CxTestingAllocator talloc;
cx_testing_allocator_init(&talloc);
CxAllocator *alloc = &talloc.base;
CX_TEST_DO {
char *str;
size_t buflen =
16;
size_t len = cx_sprintf_sa(alloc, buf, &buflen, &str,
"Hello %d %s",
4711,
"larger string");
CX_TEST_ASSERT(str != buf);
CX_TEST_ASSERT(buflen ==
25);
CX_TEST_ASSERT(len ==
24);
CX_TEST_ASSERT(
0 == memcmp(str,
"Hello 4711 larger string",
25));
cxFree(alloc, str);
CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
}
cx_testing_allocator_destroy(&talloc);
}
CX_TEST(test_sprintf_s_alloc_to_fit_terminator) {
char buf[
16];
memcpy(buf,
"0123456789abcdef",
16);
CxTestingAllocator talloc;
cx_testing_allocator_init(&talloc);
CxAllocator *alloc = &talloc.base;
CX_TEST_DO {
char *str;
size_t buflen =
16;
size_t len = cx_sprintf_sa(alloc, buf,&buflen, &str,
"Hello %d %s",
112,
"string");
CX_TEST_ASSERT(str != buf);
CX_TEST_ASSERT(len ==
16);
CX_TEST_ASSERT(buflen ==
17);
CX_TEST_ASSERT(
0 == memcmp(str,
"Hello 112 string",
17));
cxFree(alloc, str);
CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
}
cx_testing_allocator_destroy(&talloc);
}
CxTestSuite *cx_test_suite_printf(
void) {
CxTestSuite *suite = cx_test_suite_new(
"printf");
cx_test_register(suite, test_bprintf);
cx_test_register(suite, test_bprintf_large_string);
cx_test_register(suite, test_bprintf_nocap);
cx_test_register(suite, test_fprintf);
cx_test_register(suite, test_asprintf);
cx_test_register(suite, test_asprintf_large_string);
cx_test_register(suite, test_sprintf_no_realloc);
cx_test_register(suite, test_sprintf_realloc);
cx_test_register(suite, test_sprintf_realloc_to_fit_terminator);
cx_test_register(suite, test_sprintf_s_no_alloc);
cx_test_register(suite, test_sprintf_s_alloc);
cx_test_register(suite, test_sprintf_s_alloc_to_fit_terminator);
return suite;
}