diff options
Diffstat (limited to 'src/lib/test-strnum.c')
-rw-r--r-- | src/lib/test-strnum.c | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/src/lib/test-strnum.c b/src/lib/test-strnum.c new file mode 100644 index 0000000..892c579 --- /dev/null +++ b/src/lib/test-strnum.c @@ -0,0 +1,398 @@ +/* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" + + +#define INVALID(n) { #n, -1, 0 } +#define VALID(n) { #n, 0, n } + +/* always pads with leading zeros to a size of 9 digits */ +static int crappy_uintmax_to_str(char *into, uintmax_t val) +{ +#define BIGBASE 1000000000ull +#define STRINGIFY(s) #s +#define STRINGIFY2(s) STRINGIFY(s) + int len = 0; + if(val >= BIGBASE) { + len = crappy_uintmax_to_str(into, val/BIGBASE); + } + i_snprintf(into + len, 10, "%09llu", + (unsigned long long)(val % BIGBASE)); + return len + strlen(STRINGIFY2(BIGBASE))-4; +#undef STRINGIFY2 +#undef STRINGIFY +#undef BIGBASE +} +static void test_str_to_uintmax(void) +{ + unsigned int i=0; + int randrange = i_rand_minmax(1, 15); /* when 1, will max out on 1s */ + uintmax_t value = 0, valbase = i_rand() * 1000ull; + int len, ret; + char buff[50]; /* totally assumes < 159 bits */ + + test_begin("str_to_uintmax in range"); + while (i < sizeof(uintmax_t)*CHAR_BIT) { + uintmax_t value_back; + const char *endp; + + value = (value << 1) + 1; + if (value >= 64) + value -= i_rand_limit(randrange); /* don't always test the same numbers */ + len = crappy_uintmax_to_str(buff, value); + ret = str_to_uintmax(buff, &value_back); + test_assert_idx(ret == 0, i); + test_assert_idx(value == value_back, i); + + /* test with trailing noise */ + buff[len] = 'x'; /* don't even null-terminate, let's be evil */ + value_back = 0x1234567890123456; + ret = str_to_uintmax(buff, &value_back); + test_assert_idx(ret < 0, i); + test_assert_idx(value_back == 0x1234567890123456, i); + ret = str_parse_uintmax(buff, &value_back, &endp); + test_assert_idx(ret == 0, i); + test_assert_idx(value_back == value, i); + test_assert_idx(endp == &buff[len], i); + i++; + } + test_end(); + + /* not knowing exactly how large a uintmax_t is, we have to construct + the troublesome near-10/9*MAX strings manually by appending digits + to a MAX/9 string which we can easily create. Do a wider range + of 30 rather than the obvious 10, just in case - all are too large.*/ + test_begin("str_to_uintmax overflow corner case"); + value = UINTMAX_MAX/9-1; + len = crappy_uintmax_to_str(buff, value); + buff[len] = '0'; + buff[len+1] = '\0'; + for(i = 0; i <= 30; ++i) { + int j = len + 1; + while (buff[--j] == '9') + buff[j] = '0'; + buff[j]++; + value = valbase + i; + ret = str_to_uintmax(buff, &value); + test_assert_idx(ret < 0 && value == valbase + i, i); + } + test_end(); +} + +/* always pads with leading zeros to a size of 9 digits */ +static int crappy_uintmax_to_str_hex(char *into, uintmax_t val) +{ +#define BIGBASE 0x1000000000ull +#define STRINGIFY(s) #s +#define STRINGIFY2(s) STRINGIFY(s) + int len = 0; + if(val >= BIGBASE) { + len = crappy_uintmax_to_str_hex(into, val/BIGBASE); + } + i_snprintf(into + len, 10, "%09llx", + (unsigned long long)(val % BIGBASE)); + return len + strlen(STRINGIFY2(BIGBASE))-6; +#undef STRINGIFY2 +#undef STRINGIFY +#undef BIGBASE +} +static void test_str_to_uintmax_hex(void) +{ + unsigned int i=0; + int randrange = i_rand_minmax(1, 15); /* when 1, will max out on 1s */ + uintmax_t value = 0, valbase = i_rand() * 1000ull; + int len, ret; + char buff[52]; /* totally assumes < 200 bits */ + + test_begin("str_to_uintmax_hex in range"); + while (i < sizeof(uintmax_t)*CHAR_BIT) { + uintmax_t value_back; + const char *endp; + + value = (value << 1) + 1; + if (value >= 64) + value -= i_rand_limit(randrange); /* don't always test the same numbers */ + len = crappy_uintmax_to_str_hex(buff, value); + ret = str_to_uintmax_hex(buff, &value_back); + test_assert_idx(ret == 0, i); + test_assert_idx(value == value_back, i); + + /* test with trailing noise */ + buff[len] = 'x'; /* don't even null-terminate, let's be evil */ + value_back = 0x1234567890123456; + ret = str_to_uintmax_hex(buff, &value_back); + test_assert_idx(ret < 0, i); + test_assert_idx(value_back == 0x1234567890123456, i); + ret = str_parse_uintmax_hex(buff, &value_back, &endp); + test_assert_idx(ret == 0, i); + test_assert_idx(value_back == value, i); + test_assert_idx(endp == &buff[len], i); + i++; + } + test_end(); + + /* not knowing exactly how large a uintmax_t is, we have to construct + the troublesome near-0x10/0x0F*MAX strings manually by appending digits + to a MAX/0x0f string which we can easily create. Do a wider range + of 0x30 rather than the obvious 0x10, just in case - all are too large.*/ + test_begin("str_to_uintmax_hex overflow corner case"); + value = (UINTMAX_MAX/0x0f)-1; + len = crappy_uintmax_to_str_hex(buff, value); + buff[len] = '0'; + buff[len+1] = '\0'; + for(i = 0; i <= 0x30; ++i) { + int j = len + 1; + while (buff[--j] == 'f') + buff[j] = '0'; + if (buff[j] == '9') + buff[j] = 'a'; + else + buff[j]++; + value = valbase + i; + ret = str_to_uintmax_hex(buff, &value); + test_assert_idx(ret < 0 && value == valbase + i, i); + } + test_end(); +} + +/* always pads with leading zeros to a size of 9 digits */ +static int crappy_uintmax_to_str_oct(char *into, uintmax_t val) +{ +#define BIGBASE 01000000000ull +#define STRINGIFY(s) #s +#define STRINGIFY2(s) STRINGIFY(s) + int len = 0; + if(val >= BIGBASE) { + len = crappy_uintmax_to_str_oct(into, val/BIGBASE); + } + i_snprintf(into + len, 10, "%09llo", + (unsigned long long)(val % BIGBASE)); + return len + strlen(STRINGIFY2(BIGBASE))-5; +#undef STRINGIFY2 +#undef STRINGIFY +#undef BIGBASE +} +static void test_str_to_uintmax_oct(void) +{ + unsigned int i=0; + int randrange = i_rand_minmax(1, 15); /* when 1, will max out on 1s */ + uintmax_t value = 0, valbase = i_rand() * 1000ull; + int len, ret; + char buff[69]; /* totally assumes < 200 bits */ + + test_begin("str_to_uintmax_oct in range"); + while (i < sizeof(uintmax_t)*CHAR_BIT) { + uintmax_t value_back; + const char *endp = NULL; + + value = (value << 1) + 1; + if (value >= 64) + value -= i_rand_limit(randrange); /* don't always test the same numbers */ + len = crappy_uintmax_to_str_oct(buff, value); + ret = str_to_uintmax_oct(buff, &value_back); + test_assert_idx(ret == 0, i); + test_assert_idx(value == value_back, i); + + /* test with trailing noise */ + buff[len] = 'x'; /* don't even null-terminate, let's be evil */ + value_back = 0x1234567890123456; + ret = str_to_uintmax_oct(buff, &value_back); + test_assert_idx(ret < 0, i); + test_assert_idx(value_back == 0x1234567890123456, i); + ret = str_parse_uintmax_oct(buff, &value_back, &endp); + test_assert_idx(ret == 0, i); + test_assert_idx(value_back == value, i); + test_assert_idx(endp == &buff[len], i); + i++; + } + test_end(); + + /* not knowing exactly how large a uintmax_t is, we have to construct + the troublesome near-010/007*MAX strings manually by appending digits + to a MAX/007 string which we can easily create. Do a wider range + of 030 rather than the obvious 010, just in case - all are too large.*/ + test_begin("str_to_uintmax_oct overflow corner case"); + value = (UINTMAX_MAX/007)-1; + len = crappy_uintmax_to_str_oct(buff, value); + buff[len] = '0'; + buff[len+1] = '\0'; + for(i = 0; i <= 030; ++i) { + int j = len + 1; + while (buff[--j] == '7') + buff[j] = '0'; + buff[j]++; + value = valbase + i; + ret = str_to_uintmax_oct(buff, &value); + test_assert_idx(ret < 0 && value == valbase + i, i); + } + test_end(); +} + +static void test_str_to_u64(void) +{ + unsigned int i; + const struct { + const char *input; + int ret; + uint64_t val; + } u64tests[] = { + INVALID(-1), + INVALID(foo), + VALID(0), + VALID(000000000000000000000000000000000000000000000000000000000000000), + { "000000000000000000000000000000000000000000000000000001000000001", 0, 1000000001 }, + { "18446744073709551615", 0, 18446744073709551615ULL }, + INVALID(18446744073709551616), + INVALID(20496382304121724010), /* 2^64*10/9 doesn't wrap */ + INVALID(20496382304121724017), /* 2^64*10/9 wraps only after addition */ + INVALID(20496382304121724020), /* 2^64*10/9 wraps on multiply*/ + }; + test_begin("str_to_uint64"); + for (i = 0; i < N_ELEMENTS(u64tests); ++i) { + uint64_t val = 0xBADBEEF15BADF00D; + int ret = str_to_uint64(u64tests[i].input, &val); + test_assert_idx(ret == u64tests[i].ret, i); + if (ret == 0) + test_assert_idx(val == u64tests[i].val, i); + else + test_assert_idx(val == 0xBADBEEF15BADF00D, i); + + if (ret == 0) + T_BEGIN { + const char *longer = t_strconcat(u64tests[i].input, "x", NULL); + ret = str_to_uint64(longer, &val); + test_assert_idx(ret < 0, i); + } T_END; + } + test_end(); +} + +static void test_str_to_u32(void) +{ + unsigned int i; + const struct { + const char *input; + int ret; + uint32_t val; + } u32tests[] = { + VALID(0), + INVALID(-0), + VALID(4294967295), + INVALID(4294967296), + INVALID(4772185880), + INVALID(4772185884), + INVALID(4772185890), + }; + test_begin("str_to_uint32"); + for (i = 0; i < N_ELEMENTS(u32tests); ++i) { + uint32_t val = 0xDEADF00D; + int ret = str_to_uint32(u32tests[i].input, &val); + test_assert_idx(ret == u32tests[i].ret, i); + if (ret == 0) + test_assert_idx(val == u32tests[i].val, i); + else + test_assert_idx(val == 0xDEADF00D, i); + } + test_end(); +} + +/* Assumes long long is 64 bit, 2's complement */ +static void test_str_to_llong(void) +{ + unsigned int i; + const struct { + const char *input; + int ret; + long long val; + } i64tests[] = { + VALID(0), + VALID(-0), + INVALID(--0), + VALID(2147483648), + VALID(-2147483649), + VALID(9223372036854775807), + { "-9223372036854775808", 0, -9223372036854775807-1 }, + INVALID(9223372036854775808), + INVALID(-9223372036854775809), + }; + test_begin("str_to_llong"); + for (i = 0; i < N_ELEMENTS(i64tests); ++i) { + long long val = 123456789; + int ret = str_to_llong(i64tests[i].input, &val); + test_assert_idx(ret == i64tests[i].ret, i); + if (ret == 0) + test_assert_idx(val == i64tests[i].val, i); + else + test_assert_idx(val == 123456789, i); + } + test_end(); +} + +/* Assumes int is 32 bit, 2's complement */ +static void test_str_to_i32(void) +{ + unsigned int i; + const struct { + const char *input; + int ret; + int val; + } i32tests[] = { + VALID(0), + VALID(-0), + INVALID(--0), + VALID(2147483647), + VALID(-2147483648), + INVALID(2147483648), + INVALID(-2147483649), + }; + test_begin("str_to_int"); + for (i = 0; i < N_ELEMENTS(i32tests); ++i) { + int val = 123456789; + int ret = str_to_int(i32tests[i].input, &val); + test_assert_idx(ret == i32tests[i].ret, i); + if (ret == 0) + test_assert_idx(val == i32tests[i].val, i); + else + test_assert_idx(val == 123456789, i); + } + test_end(); +} + +static void test_str_is_float(void) +{ + test_begin("str_is_float accepts integer"); + /* accepts integer */ + test_assert(str_is_float("0",'\0')); + test_assert(str_is_float("1234",'\0')); + test_end(); + test_begin("str_is_float accepts float"); + test_assert(str_is_float("0.0",'\0')); + test_assert(str_is_float("1234.0",'\0')); + test_assert(str_is_float("0.1234",'\0')); + test_assert(str_is_float("1234.1234",'\0')); + test_assert(str_is_float("0.1234 ",' ')); + test_assert(str_is_float("1234.1234",'.')); + test_end(); + test_begin("str_is_float refuses invalid values"); + test_assert(!str_is_float(".",'\0')); + test_assert(!str_is_float(".1234",'\0')); + test_assert(!str_is_float("1234.",'\0')); + test_assert(!str_is_float("i am not a float at all",'\0')); + test_assert(!str_is_float("0x1234.0x1234",'\0')); + test_assert(!str_is_float(".0",'\0')); + test_assert(!str_is_float("0.",'\0')); + test_end(); +} + +void test_strnum(void) +{ + /* If the above isn't true, then we do expect some failures possibly */ + test_str_to_uintmax(); + test_str_to_uintmax_hex(); + test_str_to_uintmax_oct(); + test_str_to_u64(); + test_str_to_u32(); + test_str_to_llong(); + test_str_to_i32(); + test_str_is_float(); +} |