/*- * Copyright (c) 2014-2016 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #define T_MAGIC_STR CS("xyzzy") #define T_MAGIC_LEN (sizeof("xyzzy") - 1) #define T_LONG_MAGIC_STR CS("squeamish ossifrage") #define T_LONG_MAGIC_LEN (sizeof("squeamish ossifrage") - 1) static int t_string_noop(char **desc CRYB_UNUSED, void *arg) { string *str; (void)arg; if ((str = string_new()) == NULL) return (0); string_delete(str); return (1); } static int t_string_new(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) { string *s; int ret; ret = 1; t_malloc_fail_after = 1; ret &= t_is_not_null(s = string_new()); string_delete(s); ret &= t_is_null(s = string_new()); string_delete(s); t_malloc_fail = 0; return (ret); } /*************************************************************************** * Appending and expansion */ static struct t_append_case { const char *desc; const char_t *s; size_t ilen; ssize_t olen; int fail; } t_append_cases[] = { /* all short cases are performed with malloc failure enabled */ { .desc = "empty with limit", .s = CS(""), .ilen = 0, .olen = 0, .fail = 1, }, { .desc = "empty without limit", .s = CS(""), .ilen = SIZE_MAX, .olen = 0, .fail = 1, }, { .desc = "short with limit", .s = T_MAGIC_STR, .ilen = T_MAGIC_LEN / 2, .olen = T_MAGIC_LEN / 2, .fail = 1, }, { .desc = "short without limit", .s = T_MAGIC_STR, .ilen = SIZE_MAX, .olen = T_MAGIC_LEN, .fail = 1, }, /* expected to allocate (but we have no way to test that) */ { .desc = "long", .s = T_LONG_MAGIC_STR, .ilen = SIZE_MAX, .olen = T_LONG_MAGIC_LEN, .fail = 0, }, /* expected to try to allocate and fail */ { .desc = "long with allocation failure", .s = T_LONG_MAGIC_STR, .ilen = SIZE_MAX, .olen = -1, .fail = 1, }, }; static int t_string_append_cs(char **desc CRYB_UNUSED, void *arg) { struct t_append_case *t = arg; string *s; int ret; s = string_new(); t_malloc_fail = t->fail; ret = t_compare_ssz(t->olen, string_append_cs(s, t->s, t->ilen)); t_malloc_fail = 0; string_delete(s); return (ret); } static int t_string_append_string(char **desc CRYB_UNUSED, void *arg) { struct t_append_case *t = arg; string *s, *os; int ret; s = string_new(); string_append_cs(s, CS("!"), 1); os = string_dup_cs(t->s, SIZE_MAX); t_malloc_fail = t->fail; ret = t_compare_ssz(t->olen < 0 ? t->olen : t->olen + 1, string_append_string(s, os, t->ilen)); t_malloc_fail = 0; string_delete(os); string_delete(s); return (ret); } /*************************************************************************** * Buffers and lengths */ static struct t_buf_case { const char *desc; const char_t *s; size_t len; } t_buf_cases[] = { { .desc = "empty", .s = CS(""), .len = 0, }, { .desc = "one", .s = CS("1"), .len = 1, }, { .desc = "short", .s = T_MAGIC_STR, .len = T_MAGIC_LEN, }, { .desc = "long", .s = T_LONG_MAGIC_STR, .len = T_LONG_MAGIC_LEN, }, }; static int t_string_buf(char **desc CRYB_UNUSED, void *arg) { struct t_buf_case *t = arg; const char_t *buf; string *s; int ret; ret = 1; s = string_dup_cs(t->s, t->len); buf = string_buf(s); ret &= t_compare_mem(t->s, buf, (t->len + 1) * sizeof(char_t)); string_append_cs(s, t->s, t->len); buf = string_buf(s); ret &= t_compare_mem(t->s, buf, t->len * sizeof(char_t)) & t_compare_mem(t->s, buf + t->len, (t->len + 1) * sizeof(char_t)); string_delete(s); return (ret); } static int t_string_len(char **desc CRYB_UNUSED, void *arg) { struct t_buf_case *t = arg; string *s; int ret; ret = 1; s = string_dup_cs(t->s, t->len); ret = t_compare_i(t->len, string_len(s)); string_append_cs(s, t->s, t->len); ret &= t_compare_i(t->len + t->len, string_len(s)); string_delete(s); return (ret); } /*************************************************************************** * Comparisons */ static struct t_compare_case { const char *desc; const char_t *s1, *s2; int cmp; } t_compare_cases[] = { { "empty with empty", CS(""), CS(""), 0, }, { "empty with non-empty", CS(""), CS("xyzzy"), -1, }, { "non-empty with empty", CS("xyzzy"), CS(""), 1, }, { "non-empty with same non-empty", CS("xyzzy"), CS("xyzzy"), 0, }, { "non-empty with later non-empty", CS("abba"), CS("baba"), -1, }, { "non-empty with earlier non-empty", CS("baba"), CS("abba"), 1, }, { "non-empty prefix with non-empty", CS("baba"), CS("babaorum"), -1, }, { "non-empty with non-empty prefix", CS("babaorum"), CS("baba"), 1, }, }; static int t_string_compare(char **desc CRYB_UNUSED, void *arg) { struct t_compare_case *t = arg; string *s1, *s2; int ret; s1 = string_dup_cs(t->s1, SIZE_MAX); s2 = string_dup_cs(t->s2, SIZE_MAX); ret = t_compare_i(t->cmp, string_compare(s1, s2)); string_delete(s2); string_delete(s1); return (ret); } static int t_string_compare_cs(char **desc CRYB_UNUSED, void *arg) { struct t_compare_case *t = arg; string *s1; int ret; s1 = string_dup_cs(t->s1, SIZE_MAX); ret = t_compare_i(t->cmp, string_compare_cs(s1, t->s2, SIZE_MAX)); string_delete(s1); return (ret); } static int t_string_equal(char **desc CRYB_UNUSED, void *arg) { struct t_compare_case *t = arg; string *s1, *s2; int ret = 1; s1 = string_dup_cs(t->s1, SIZE_MAX); s2 = string_dup_cs(t->s2, SIZE_MAX); ret = t_compare_i(!!t->cmp, string_equal(s1, s2) == 0); string_delete(s2); string_delete(s1); return (ret); } static int t_string_equal_cs(char **desc CRYB_UNUSED, void *arg) { struct t_compare_case *t = arg; string *s1; int ret = 1; s1 = string_dup_cs(t->s1, SIZE_MAX); ret = t_compare_i(!!t->cmp, string_equal_cs(s1, t->s2, SIZE_MAX) == 0); string_delete(s1); return (ret); } /*************************************************************************** * Miscellaneous functions */ static int t_string_trunc(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED) { string *s; ssize_t len; int ret; s = string_dup_cs(T_MAGIC_STR, SIZE_MAX); len = string_len(s); ret = t_compare_ssz(T_MAGIC_LEN, len) & t_compare_ssz(len, string_trunc(s, SIZE_MAX)) & t_compare_ssz(len, string_trunc(s, len + 1)) & t_compare_ssz(len, string_trunc(s, len)) & t_compare_ssz(len - 1, string_trunc(s, len - 1)); string_delete(s); return (ret); } /*************************************************************************** * Boilerplate */ static int t_prepare(int argc, char *argv[]) { unsigned int i; (void)argc; (void)argv; t_malloc_fatal = 1; t_add_test(t_string_noop, NULL, "no-op"); t_add_test(t_string_new, NULL, "string_new"); for (i = 0; i < sizeof t_append_cases / sizeof *t_append_cases; ++i) t_add_test(t_string_append_cs, &t_append_cases[i], "%s (%s)", "string_append_cs", t_append_cases[i].desc); for (i = 0; i < sizeof t_append_cases / sizeof *t_append_cases; ++i) t_add_test(t_string_append_string, &t_append_cases[i], "%s (%s)", "string_append_string", t_append_cases[i].desc); for (i = 0; i < sizeof t_buf_cases / sizeof *t_buf_cases; ++i) t_add_test(t_string_len, &t_buf_cases[i], "%s (%s)", "string_len", t_buf_cases[i].desc); for (i = 0; i < sizeof t_buf_cases / sizeof *t_buf_cases; ++i) t_add_test(t_string_buf, &t_buf_cases[i], "%s (%s)", "string_buf", t_buf_cases[i].desc); // t_add_test(t_string_dup_cs, NULL, "string_dup_cs"); for (i = 0; i < sizeof t_compare_cases / sizeof *t_compare_cases; ++i) { t_add_test(t_string_compare, &t_compare_cases[i], "%s (%s)", "string_compare", t_compare_cases[i].desc); t_add_test(t_string_compare_cs, &t_compare_cases[i], "%s (%s)", "string_compare_cs", t_compare_cases[i].desc); } for (i = 0; i < sizeof t_compare_cases / sizeof *t_compare_cases; ++i) { t_add_test(t_string_equal, &t_compare_cases[i], "%s (%s)", "string_equal", t_compare_cases[i].desc); t_add_test(t_string_equal_cs, &t_compare_cases[i], "%s (%s)", "string_equal_cs", t_compare_cases[i].desc); } t_add_test(t_string_trunc, NULL, "string_trunc"); return (0); } int main(int argc, char *argv[]) { t_main(t_prepare, NULL, argc, argv); }