#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <upanic.h>
#include <umem.h>
#include <sys/ilstr.h>
#include <sys/sysmacros.h>
#include <sys/debug.h>
#define VERIFYSTRING(ils, expected) \
do { \
const char *check = ilstr_cstr(ils); \
VERIFY(check != NULL); \
if (strcmp(check, (expected)) != 0) { \
char buf[2048]; \
(void) snprintf(buf, sizeof (buf), \
"\"%s\" != expected \"%s\"", \
check, (expected)); \
assfail(buf, __FILE__, __LINE__); \
} \
} while (0)
typedef enum ilstr_test_types {
ITT_STD = 0x01,
ITT_PRE = 0x02,
} ilstr_test_types_t;
#define ITT_ALL (ITT_STD | ITT_PRE)
typedef struct ilstr_test {
char *ist_name;
int (*ist_func)(ilstr_t *ils);
uint_t ist_trials;
ilstr_test_types_t ist_types;
} ilstr_test_t;
#define PREALLOC_SZ 1024
static char ilsbuf[PREALLOC_SZ];
const char *
_umem_debug_init(void)
{
return ("default,verbose");
}
const char *
_umem_logging_init(void)
{
return ("transaction,contents,fail");
}
int
ist_empty(ilstr_t *ils)
{
VERIFY3U(ilstr_len(ils), ==, 0);
VERIFY(ilstr_is_empty(ils));
VERIFY(ilstr_cstr(ils) != NULL);
VERIFY3U(ilstr_cstr(ils)[0], ==, '\0');
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
return (0);
}
int
ist_prealloc_toobig(ilstr_t *ils)
{
for (uint_t n = 0; n < PREALLOC_SZ - 1; n++) {
ilstr_append_str(ils, "A");
}
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
ilstr_append_str(ils, "A");
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
ilstr_append_str(ils, "A");
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
ilstr_prepend_char(ils, 'A');
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
ilstr_reset(ils);
ilstr_append_str(ils, "B");
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
VERIFYSTRING(ils, "B");
ilstr_reset(ils);
for (uint_t n = 0; n < PREALLOC_SZ - 1; n++) {
ilstr_append_str(ils, "C");
}
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
ilstr_prepend_str(ils, "D");
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
ilstr_prepend_str(ils, "D");
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
ilstr_append_char(ils, 'D');
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
return (0);
}
int
ist_standard_toobig(ilstr_t *ils)
{
for (uint_t n = 0; n < PREALLOC_SZ - 1; n++) {
ilstr_append_str(ils, "A");
}
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
for (uint_t n = 0; n < PREALLOC_SZ - 1; n++) {
umem_setmtbf(1);
ilstr_append_str(ils, "A");
umem_setmtbf(0);
if (ilstr_errno(ils) == ILSTR_ERROR_NOMEM) {
break;
}
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
}
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
VERIFY3U(ilstr_len(ils), <, 2 * PREALLOC_SZ);
return (0);
}
int
ist_huge(ilstr_t *ils)
{
uint_t target = 26 * 1024 * 1024;
for (uint_t n = 0; n < target / 26; n++) {
ilstr_append_str(ils, "abcdefghijklmnopqrstuvwxyz");
if (ilstr_errno(ils) == ILSTR_ERROR_NOMEM) {
return (ENOMEM);
}
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
}
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
VERIFY3U(ilstr_len(ils), ==, target);
VERIFY(!ilstr_is_empty(ils));
return (0);
}
int
ist_printf_1(ilstr_t *ils)
{
const char *want = "a\nb\n1000\ntest string\n";
ilstr_aprintf(ils, "a\nb\n%u\n%s\n", 1000, "test string");
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
VERIFYSTRING(ils, want);
return (0);
}
int
ist_printf_2(ilstr_t *ils)
{
int r = 0;
const char *lorem = "Lorem ipsum dolor sit amet, consectetur "
"adipiscing elit, sed do eiusmod tempor incididunt ut labore "
"et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud "
"exercitation ullamco laboris nisi ut aliquip ex ea commodo "
"consequat.";
char *want;
if (asprintf(&want, "%s\n\tnumber 1\n%s\n\n%s\n number 100000000\n",
lorem, lorem, lorem) < 0) {
return (errno);
}
ilstr_aprintf(ils, "%s\n\t", lorem);
ilstr_append_str(ils, "number");
ilstr_aprintf(ils, " %u\n%s\n\n", 1, lorem);
ilstr_append_str(ils, lorem);
ilstr_aprintf(ils, "\n number %lld\n", (long long)100000000);
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
if (strcmp(ilstr_cstr(ils), want) != 0) {
printf("want: %s\n", want);
printf("got: %s\n", ilstr_cstr(ils));
r = ENOENT;
}
free(want);
return (r);
}
int
ist_resets(ilstr_t *ils)
{
VERIFYSTRING(ils, "");
ilstr_reset(ils);
VERIFYSTRING(ils, "");
ilstr_append_str(ils, "abc");
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
VERIFYSTRING(ils, "abc");
ilstr_append_str(ils, "def");
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
VERIFYSTRING(ils, "abcdef");
ilstr_reset(ils);
VERIFYSTRING(ils, "");
ilstr_append_str(ils, "xyz");
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
VERIFYSTRING(ils, "xyz");
ilstr_reset(ils);
VERIFY(strcmp(ilstr_cstr(ils), "") == 0);
VERIFYSTRING(ils, "");
return (0);
}
int
ist_random(ilstr_t *ils)
{
char *work;
uint_t target = 256 + arc4random_uniform(1024 - 256);
printf(" - target string length %u\n", target);
if ((work = calloc(1, 1024 + 1)) == NULL) {
return (errno);
}
VERIFY3U(ilstr_len(ils), ==, 0);
VERIFY(ilstr_is_empty(ils));
VERIFY3U(ilstr_cstr(ils)[0], ==, '\0');
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
for (uint_t n = 0; n < target; n++) {
char c[2] = { arc4random_uniform('Z' - 'A') + 'A', '\0' };
work[n] = c[0];
ilstr_append_str(ils, c);
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
VERIFY3U(ilstr_len(ils), ==, n + 1);
VERIFY(!ilstr_is_empty(ils));
VERIFYSTRING(ils, work);
}
VERIFY(!ilstr_is_empty(ils));
VERIFY3U(ilstr_len(ils), ==, target);
VERIFYSTRING(ils, work);
printf(" - final string: %s\n", work);
free(work);
return (0);
}
int
ist_prepend_char(ilstr_t *ils)
{
ilstr_append_str(ils, "ackwards");
ilstr_prepend_char(ils, 'B');
ilstr_append_char(ils, '!');
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
VERIFYSTRING(ils, "Backwards!");
return (0);
}
int
ist_prepend_str(ilstr_t *ils)
{
ilstr_prepend_str(ils, "string was prepended");
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
VERIFYSTRING(ils, "string was prepended");
ilstr_prepend_str(ils, "this ");
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
VERIFYSTRING(ils, "this string was prepended");
ilstr_append_str(ils, ", successfully");
VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
VERIFYSTRING(ils, "this string was prepended, successfully");
return (0);
}
int
ist_building_list(ilstr_t *ils)
{
const char *items[] = { "one", "two", "three" };
const char *expect[] = {
"empty list",
"populated list (one)",
"populated list (one, two)",
"populated list (one, two, three)",
};
for (uint_t n = 0; n <= 3; n++) {
ilstr_reset(ils);
for (uint_t i = 0; i < n; i++) {
if (!ilstr_is_empty(ils)) {
ilstr_append_str(ils, ", ");
}
ilstr_append_str(ils, items[i]);
}
if (ilstr_is_empty(ils)) {
ilstr_append_str(ils, "empty list");
} else {
ilstr_prepend_str(ils, "populated list (");
ilstr_append_str(ils, ")");
}
VERIFYSTRING(ils, expect[n]);
}
return (0);
}
uint_t
ist_drive_test(const ilstr_test_t *ist)
{
uint_t nfails = 0;
int r;
ilstr_t ils;
for (uint_t n = 0; n < ist->ist_trials; n++) {
if (ist->ist_types & ITT_STD) {
ilstr_init(&ils, 0);
printf("STD[%s]... run %d\n", ist->ist_name, n);
if ((r = ist->ist_func(&ils)) != 0) {
(void) fprintf(stderr,
"TEST FAILED: STD[%s]: %s\n",
ist->ist_name, strerror(r));
nfails += 1;
} else {
printf("TEST PASSED: STD[%s]\n",
ist->ist_name);
}
ilstr_fini(&ils);
printf("\n");
}
if (ist->ist_types & ITT_PRE) {
ilstr_init_prealloc(&ils, ilsbuf, sizeof (ilsbuf));
printf("PRE[%s]... run %d\n", ist->ist_name, n);
if ((r = ist->ist_func(&ils)) != 0) {
(void) fprintf(stderr,
"TEST FAILED: PRE[%s]: %s\n",
ist->ist_name, strerror(r));
nfails += 1;
} else {
printf("TEST PASSED: PRE[%s]\n",
ist->ist_name);
}
ilstr_fini(&ils);
printf("\n");
}
}
return (nfails);
}
static const ilstr_test_t ilstr_tests[] = {
{ "empty", ist_empty, 1, ITT_ALL },
{ "resets", ist_resets, 1, ITT_ALL },
{ "printf-1", ist_printf_1, 1, ITT_ALL },
{ "printf-2", ist_printf_2, 1, ITT_ALL },
{ "prealloc_toobig", ist_prealloc_toobig, 1, ITT_PRE },
{ "standard_toobig", ist_standard_toobig, 1, ITT_STD },
{ "prepend_char", ist_prepend_char, 1, ITT_ALL },
{ "prepend_str", ist_prepend_str, 1, ITT_ALL },
{ "building_list", ist_building_list, 1, ITT_ALL },
{ "random", ist_random, 1000, ITT_ALL },
{ "huge", ist_huge, 100, ITT_STD },
};
int
main(void)
{
uint_t nfails = 0;
for (uint_t i = 0; i < ARRAY_SIZE(ilstr_tests); i++) {
nfails += ist_drive_test(&ilstr_tests[i]);
}
char *aoe;
if ((aoe = getenv("PANIC_ON_EXIT")) != NULL && strcmp(aoe, "1") == 0) {
const char *msg = "PANIC_ON_EXIT set; panicking for findleaks";
upanic(msg, strlen(msg));
}
return (nfails == 0 ? 0 : 1);
}