summaryrefslogtreecommitdiffstats
path: root/src/lib/test-strnum.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/test-strnum.c')
-rw-r--r--src/lib/test-strnum.c398
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();
+}