/* String handling tests Copyright (C) 2001-2007, 2009, Joe Orton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "config.h" #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_ERRNO_H #include /* for the ENOENT definitions in str_errors */ #endif #include "ne_string.h" #include "ne_utils.h" #include "tests.h" #undef ONCMP #define ONCMP(a,b) ONV(strcmp(a, b), \ ("result was [%s] not [%s]", a, b)) static int simple(void) { ne_buffer *s = ne_buffer_create(); ON(s == NULL); ne_buffer_zappend(s, "abcde"); ONCMP(s->data, "abcde"); ON(ne_buffer_size(s) != 5); ne_buffer_destroy(s); return OK; } static int buf_concat(void) { ne_buffer *s = ne_buffer_create(); ON(s == NULL); ne_buffer_concat(s, "a", "b", "c", "d", "e", "f", "g", NULL); ONCMP(s->data, "abcdefg"); ON(ne_buffer_size(s) != 7); ne_buffer_destroy(s); return OK; } static int buf_concat2(void) { #define RES "alphabetagammadeltaepsilonetatheta" ne_buffer *s = ne_buffer_create(); ON(s == NULL); ne_buffer_concat(s, "alpha", "beta", "gamma", "delta", "epsilon", "eta", "theta", NULL); ONCMP(s->data, RES); ON(ne_buffer_size(s) != strlen(RES)); ne_buffer_destroy(s); return OK; } static int buf_concat3(void) { ne_buffer *s = ne_buffer_create(); ON(s == NULL); ne_buffer_zappend(s, "foobar"); ne_buffer_concat(s, "norman", NULL); ONCMP(s->data, "foobarnorman"); ON(ne_buffer_size(s) != 12); ne_buffer_destroy(s); return OK; } static int append(void) { ne_buffer *s = ne_buffer_create(); ON(s == NULL); ne_buffer_append(s, "a", 1); ne_buffer_append(s, "b", 1); ne_buffer_append(s, "c", 1); ONCMP(s->data, "abc"); ON(ne_buffer_size(s) != 3); ne_buffer_zappend(s, "hello"); ONCMP(s->data, "abchello"); ne_buffer_czappend(s, "world"); ONCMP(s->data, "abchelloworld"); ON(ne_buffer_size(s) != 13); ne_buffer_destroy(s); return OK; } static int grow(void) { ne_buffer *s = ne_buffer_ncreate(2); ON(s == NULL); ne_buffer_append(s, "a", 1); ne_buffer_grow(s, 4); ONCMP(s->data, "a"); ne_buffer_destroy(s); return OK; } static int alter(void) { ne_buffer *s = ne_buffer_create(); char *d; ON(s == NULL); ne_buffer_zappend(s, "abcdefg"); d = s->data; ON(d == NULL); d[2] = '\0'; ne_buffer_altered(s); ONCMP(s->data, "ab"); ON(ne_buffer_size(s) != 2); ne_buffer_zappend(s, "hijkl"); ONCMP(s->data, "abhijkl"); ne_buffer_destroy(s); return OK; } /* Macros for testing ne_token. */ #define TEST(res) do { \ char *tok = ne_token(&pnt, ','); \ ONN(res ": return", tok == NULL); \ ONN(res ": compare", strcmp(tok, (res))); \ ONN(res ": modify", pnt == NULL); \ } while (0) #define LASTTEST(res) do { \ char *tok = ne_token(&pnt, ','); \ ONN(res ": last return", tok == NULL); \ ONN(res ": last compare", strcmp(tok, (res))); \ ONN(res ": last modify", pnt != NULL); \ } while (0) #define QTEST(res) do { \ char *tok = ne_qtoken(&pnt, ',', QUOTES); \ ONN(res ": return", tok == NULL); \ ONN(res ": compare", strcmp(tok, (res))); \ ONN(res ": modify", pnt == NULL); \ } while (0) #define QLASTTEST(res) do { \ char *tok = ne_qtoken(&pnt, ',', QUOTES); \ ONN(res ": last return", tok == NULL); \ ONN(res ": last compare", strcmp(tok, (res))); \ ONN(res ": last modify", pnt != NULL); \ } while (0) static int token1(void) { char *str = ne_strdup("a,b,c,d"), *pnt = str; TEST("a"); TEST("b"); TEST("c"); LASTTEST("d"); ne_free(str); return OK; } static int token2(void) { char *str = ne_strdup("norman,fishing, elsewhere"), *pnt = str; TEST("norman"); TEST("fishing"); LASTTEST(" elsewhere"); ne_free(str); return OK; } static int nulls(void) { char *str = ne_strdup("alpha,,gamma"), *pnt = str; TEST("alpha"); TEST(""); LASTTEST("gamma"); ne_free(str); pnt = str = ne_strdup(",,,wooo"); TEST(""); TEST(""); TEST(""); LASTTEST("wooo"); ne_free(str); pnt = str = ne_strdup("wooo,,,"); TEST("wooo"); TEST(""); TEST(""); LASTTEST(""); ne_free(str); return OK; } static int empty(void) { char *str = ne_strdup(""), *pnt = str; LASTTEST(""); ne_free(str); return OK; } #undef QUOTES #define QUOTES "'" static int quoted(void) { char *str = ne_strdup("alpha,'beta, a fish called HELLO!?',sandwiches"); char *pnt = str; QTEST("alpha"); QTEST("'beta, a fish called HELLO!?'"); QLASTTEST("sandwiches"); ne_free(str); return OK; } static int badquotes(void) { char *str = ne_strdup("alpha,'blah"), *pnt = str; QTEST("alpha"); ON(ne_qtoken(&pnt, ',', QUOTES) != NULL); ne_free(str); return OK; } /* for testing ne_shave. */ #undef TEST #define TEST(str, ws, res) do { \ char *s = ne_strdup((str)); \ char *r = ne_shave(s, (ws)); \ ONN("[" str "]", strcmp(r, (res))); \ ne_free(s); \ } while (0) static int shave(void) { TEST(" b ", " ", "b"); TEST("b", " ", "b"); TEST(" b ", " ", "b"); TEST("--bbb-----", "-", "bbb"); TEST("hello, world ", " ", "hello, world"); TEST("<<><<<><<<<<><<", "<>", "this is foo"); TEST("09809812342347I once saw an helicopter0012312312398", "0123456789", "I once saw an helicopter"); return OK; } /* Regression test for ne_shave call which should produce an empty * string. */ static int shave_regress(void) { TEST("\"\"", "\"", ""); return OK; } /* Test the ne_token/ne_shave combination. */ #undef TEST #undef LASTTEST #define TEST(res) do { \ char *tok = ne_token(&pnt, ','); \ ONN(res ": return", tok == NULL); \ tok = ne_shave(tok, " "); \ ONN(res ": shave", tok == NULL); \ ONN(res ": compare", strcmp(tok, (res))); \ ONN(res ": modify", pnt == NULL); \ } while (0) #define LASTTEST(res) do { \ char *tok = ne_token(&pnt, ','); \ ONN(res ": last return", tok == NULL); \ tok = ne_shave(tok, " "); \ ONN(res ": last shave", tok == NULL); \ ONN(res ": last compare", strcmp(tok, (res))); \ ONN(res ": last modify", pnt != NULL); \ } while (0) /* traditional use of ne_token/ne_shave. */ static int combo(void) { char *str = ne_strdup(" fred , mary, jim , alice, david"), *pnt = str; TEST("fred"); TEST("mary"); TEST("jim"); TEST("alice"); LASTTEST("david"); ne_free(str); return 0; } static int concat(void) { #define CAT(res, args) do { char *str = ne_concat args; \ ONCMP(str, res); \ ne_free(str); } while (0) CAT("alphabeta", ("alpha", "beta", NULL)); CAT("alpha", ("alpha", "", "", NULL)); CAT("", ("", NULL)); CAT("", ("", "", "", NULL)); CAT("alpha", ("", "a", "lph", "", "a", NULL)); return OK; } static int str_errors(void) { char expect[200], actual[200]; strncpy(expect, strerror(ENOENT), sizeof(expect)-1); ONN("ne_strerror did not return passed-in buffer", ne_strerror(ENOENT, actual, sizeof(actual)) != actual); ONV(strcmp(expect, actual), ("error from ENOENT was `%s' not `%s'", actual, expect)); /* Test truncated error string is still NUL-terminated. */ ne_strerror(ENOENT, actual, 6); NE_DEBUG(NE_DBG_HTTP, "error: %s\n", actual); ONN("truncated string had wrong length", strlen(actual) != 5); ne_strerror(-1, actual, 6); ONN("truncated string for bad error had wrong length", strlen(actual) != 5); return OK; } static int strnzcpy(void) { char buf[5]; ne_strnzcpy(buf, "abcdefghi", sizeof buf); ONV(strcmp(buf, "abcd"), ("result was `%s' not `abcd'", buf)); ne_strnzcpy(buf, "ab", sizeof buf); ONV(strcmp(buf, "ab"), ("result was `%s' not `ab'", buf)); return OK; } #define FOX_STRING "The quick brown fox jumped over the lazy dog" #define PUNC_STRING "<>,.;'#:@~[]{}!\"$%^&*()_+-=" static int cleaner(void) { static const char *strings[] = { "alpha", "alpha", "pretty\033[41mcolours", "pretty [41mcolours", "beta\n", "beta ", "del\rt\na", "del t a", FOX_STRING, FOX_STRING, "0123456789", "0123456789", PUNC_STRING, PUNC_STRING, "\01blah blee\05bloo", " blah blee bloo", NULL, }; unsigned int n; for (n = 0; strings[n]; n+=2) { char *act = ne_strclean(ne_strdup(strings[n])); ONV(strcmp(act, strings[n+1]), ("cleansed to `%s' not `%s'", act, strings[n+1])); ne_free(act); } return OK; } /* Check that raw data 'raw', of length 'len', has base64 encoding * of 'expected'. */ static int b64_check(const unsigned char *raw, size_t len, const char *expected) { char *encoded = ne_base64(raw, len); unsigned char *decoded; size_t dlen; ONV(strcmp(encoded, expected), ("base64(\"%s\") gave \"%s\" not \"%s\"", raw, encoded, expected)); dlen = ne_unbase64(encoded, &decoded); ONV(dlen != len, ("decoded `%s' length was %" NE_FMT_SIZE_T " not %" NE_FMT_SIZE_T, expected, dlen, len)); ONV(memcmp(raw, decoded, dlen), ("decoded `%s' as `%.*s' not `%.*s'", expected, (int)dlen, decoded, (int)dlen, raw)); ne_free(decoded); ne_free(encoded); return OK; } /* ALLBITS: base64 encoding of "\0..\377" */ #define ALLBITS \ "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKiss" \ "LS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZ" \ "WltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWG" \ "h4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKz" \ "tLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g" \ "4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==" static int base64(void) { unsigned char bits[256]; size_t n; #define B64B(x, l, y) CALL(b64_check((unsigned char *)x, l, y)) #define B64(x, y) B64B(x, strlen(x), y) /* invent these with * $ printf "string" | uuencode -m blah */ B64("a", "YQ=="); B64("bb", "YmI="); B64("ccc", "Y2Nj"); B64("Hello, world", "SGVsbG8sIHdvcmxk"); B64("Aladdin:open sesame", "QWxhZGRpbjpvcGVuIHNlc2FtZQ=="); B64("I once saw a dog called norman.\n", "SSBvbmNlIHNhdyBhIGRvZyBjYWxsZWQgbm9ybWFuLgo="); B64("The quick brown fox jumped over the lazy dog", "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUgbGF6eSBkb2c="); /* binary data.. * $ printf "string" | wc -c # get the length * $ printf "string" | uuencode -m blah # get the base64 */ B64B("\0\0\0\0\0\n", 6, "AAAAAAAK"); B64B("I once wished \0 upon a \0 fish.", 30, "SSBvbmNlIHdpc2hlZCAAIHVwb24gYSAAIGZpc2gu"); B64B("\201\202\203\204", 4, "gYKDhA=="); for (n = 0; n < sizeof bits; n++) bits[n] = (unsigned char)n; CALL(b64_check(bits, sizeof bits, ALLBITS)); #undef B64 #undef B64B return OK; } static int unbase64(void) { static const char *ts[] = { "", "a", "ab", "abc", "}bcd", "a}cd", "ab}d", "abc}", " ", "^bcd", "a^cd", "ab^d", "abc^", "====", "=bcd", "a=cd", "ab=d", "a==d", "a=c=", NULL }; size_t n; for (n = 0; ts[n]; n++) { unsigned char *tmp; ONV(ne_unbase64(ts[n], &tmp) != 0, ("invalid string `%s' was decoded", ts[n])); } return OK; } static int printing(void) { struct { const char *in, *out; size_t pass, ret; } ts[] = { { "alpha", "alpha", 10, 5 }, { "alpha", "alph", 5, 4 }, { "foobar", "", 1, 0 }, { NULL, NULL, 0, 0} }; size_t n; for (n = 0; ts[n].in; n++) { char buf[512]; size_t ret; memset(buf, 'A', sizeof buf); ret = ne_snprintf(buf, ts[n].pass, "%s", ts[n].in); ONCMP(buf, ts[n].out); ONV(ret != ts[n].ret, ("got return value %" NE_FMT_SIZE_T " not %" NE_FMT_SIZE_T, ret, ts[n].ret)); /* byte past the NUL must still be 'A' */ ONN("buffer over-ran!", buf[ret + 1] != 'A'); } return OK; } static int casecmp(void) { static const struct { const char *left, *right; int expect; } ts[] = { { "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0 }, { "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz", 0 }, { "foo", "bar", 1 }, { "!#:[@\377", "!#:[@\377", 0 }, { "bar", "foo", -1 }, { "foop", "foo", 1 }, { "foo", "foop", -1 }, { NULL, NULL, 0 } }; size_t n; for (n = 0; ts[n].left; n++) { int actual; actual = ne_strcasecmp(ts[n].left, ts[n].right); ONV(ts[n].expect == 0 && actual != 0, ("strcasecmp(%s, %s) gave %d, expected 0", ts[n].left, ts[n].right, actual)); ONV(ts[n].expect > 0 && actual <= 0, ("strcasecmp(%s, %s) gave %d, expected > 0", ts[n].left, ts[n].right, actual)); ONV(ts[n].expect < 0 && actual >= 0, ("strcasecmp(%s, %s) gave %d, expected < 0", ts[n].left, ts[n].right, actual)); } ONN("comparison of identical pointers did not give zero", ne_strcasecmp(ts[0].left, ts[0].left) != 0); return OK; } static int casencmp(void) { static const struct { const char *left, *right; size_t n; int expect; } ts[] = { { "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 30, 0 }, { "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 10, 0 }, { "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz", 0, 0 }, { "foo", "bar", 3, 1 }, { "bar", "foo", 4, -1 }, { "bar", "foo", 3, -1 }, { "foop", "foo", 4, 1 }, { "foo", "foop", 4, -1 }, { "bee", "bar", 0, 0}, { NULL, NULL, 0, 0 } }; size_t n; for (n = 0; ts[n].left; n++) { int actual; actual = ne_strncasecmp(ts[n].left, ts[n].right, ts[n].n); ONV(ts[n].expect == 0 && actual != 0, ("strncasecmp(%s, %s, %" NE_FMT_SIZE_T ") gave %d, expected 0", ts[n].left, ts[n].right, ts[n].n, actual)); ONV(ts[n].expect > 0 && actual <= 0, ("strncasecmp(%s, %s, %" NE_FMT_SIZE_T ") gave %d, expected > 0", ts[n].left, ts[n].right, ts[n].n, actual)); ONV(ts[n].expect < 0 && actual >= 0, ("strncasecmp(%s, %s, %" NE_FMT_SIZE_T ") gave %d, expected < 0", ts[n].left, ts[n].right, ts[n].n, actual)); } ONN("comparison of identical pointers did not give zero", ne_strncasecmp(ts[0].left, ts[0].left, 5) != 0); return OK; } static int buf_print(void) { ne_buffer *buf = ne_buffer_create(); ne_buffer_czappend(buf, "foo-"); ne_buffer_snprintf(buf, 20, "bar-%s-asda", "norman"); ne_buffer_czappend(buf, "-bloo"); ONN("snprintf return value", ne_buffer_snprintf(buf, 2, "---") != 1); ONCMP(buf->data, "foo-bar-norman-asda-bloo-"); ne_buffer_destroy(buf); return OK; } static int qappend(void) { static const struct { const char *in; size_t inlen; const char *out; } ts[] = { { "", 0, "" }, { "a", 1, "a" }, { "b", 2, "b\\x00" }, { "alpha\0alpha", 11, "alpha\\x00alpha" }, { "a\tb", 3, "a\\x09b" }, { NULL } }; unsigned n; for (n = 0; ts[n].in; n++) { ne_buffer *buf = ne_buffer_create(); char *s; const unsigned char *in = (const unsigned char *)ts[n].in; ne_buffer_qappend(buf, in, ts[n].inlen); ONCMP(buf->data, ts[n].out); ONV(strlen(buf->data) + 1 != buf->used, ("bad buffer length for '%s': %" NE_FMT_SIZE_T, ts[n].out, buf->used)); s = ne_strnqdup(in, ts[n].inlen); ONCMP(s, ts[n].out); ne_free(s); ne_buffer_destroy(buf); } return OK; } ne_test tests[] = { T(simple), T(buf_concat), T(buf_concat2), T(buf_concat3), T(append), T(grow), T(alter), T(token1), T(token2), T(nulls), T(empty), T(quoted), T(badquotes), T(shave), T(shave_regress), T(combo), T(concat), T(str_errors), T(strnzcpy), T(cleaner), T(base64), T(unbase64), T(printing), T(casecmp), T(casencmp), T(buf_print), T(qappend), T(NULL) };