diff options
Diffstat (limited to 'src/lib/test-time-util.c')
-rw-r--r-- | src/lib/test-time-util.c | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/src/lib/test-time-util.c b/src/lib/test-time-util.c new file mode 100644 index 0000000..675a5db --- /dev/null +++ b/src/lib/test-time-util.c @@ -0,0 +1,406 @@ +/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "time-util.h" + +#include <time.h> + +static void test_timeval_cmp(void) +{ + static const struct { + struct timeval tv1, tv2; + int output; + } tests[] = { + { + .tv1 = { 0, 0 }, + .tv2 = { 0, 0 }, + .output = 0, + }, { + .tv1 = { INT_MAX, 999999 }, + .tv2 = { INT_MAX, 999999 }, + .output = 0, + }, { + .tv1 = { 0, 0 }, + .tv2 = { 0, 1 }, + .output = -1, + }, { + .tv1 = { 0, 0 }, + .tv2 = { 1, 0 }, + .output = -1, + }, { + .tv1 = { 0, 999999 }, + .tv2 = { 1, 0 }, + .output = -1, + }, { + .tv1 = { 1, 0 }, + .tv2 = { 1, 1 }, + .output = -1, + }, { + .tv1 = { -INT_MAX, 0 }, + .tv2 = { INT_MAX, 0 }, + .output = -1, + } + }; + unsigned int i; + + test_begin("timeval_cmp()"); + for (i = 0; i < N_ELEMENTS(tests); i++) { + const struct timeval *tv1 = &tests[i].tv1, *tv2 = &tests[i].tv2; + int output = tests[i].output; + + test_assert(timeval_cmp(tv1, tv2) == output); + test_assert(timeval_cmp(tv2, tv1) == -output); + } + test_end(); +} + +static void test_timeval_cmp_margin(void) +{ + static const struct { + struct timeval tv1, tv2; + unsigned int margin; + int output; + } tests[] = { + { + .tv1 = { 0, 0 }, + .tv2 = { 0, 0 }, + .output = 0, + },{ + .tv1 = { INT_MAX, 999999 }, + .tv2 = { INT_MAX, 999999 }, + .output = 0, + + },{ + .tv1 = { 0, 0 }, + .tv2 = { 0, 1 }, + .output = -1, + },{ + .tv1 = { 0, 0 }, + .tv2 = { 1, 0 }, + .output = -1, + },{ + .tv1 = { 0, 999999 }, + .tv2 = { 1, 0 }, + .output = -1, + },{ + .tv1 = { 1, 0 }, + .tv2 = { 1, 1 }, + .output = -1, + },{ + .tv1 = { -INT_MAX, 0 }, + .tv2 = { INT_MAX, 0 }, + .output = -1, + },{ + .tv1 = { 0, 999999 }, + .tv2 = { 1, 0 }, + .margin = 1, + .output = 0, + },{ + .tv1 = { 1, 0 }, + .tv2 = { 1, 1 }, + .margin = 1, + .output = 0, + },{ + .tv1 = { 0, 999998 }, + .tv2 = { 1, 0 }, + .margin = 1, + .output = -1, + },{ + .tv1 = { 1, 0 }, + .tv2 = { 1, 2 }, + .margin = 1, + .output = -1, + },{ + .tv1 = { 0, 998000 }, + .tv2 = { 1, 0 }, + .margin = 2000, + .output = 0, + },{ + .tv1 = { 1, 0 }, + .tv2 = { 1, 2000 }, + .margin = 2000, + .output = 0, + },{ + .tv1 = { 0, 997999 }, + .tv2 = { 1, 0 }, + .margin = 2000, + .output = -1, + },{ + .tv1 = { 1, 0 }, + .tv2 = { 1, 2001 }, + .margin = 2000, + .output = -1, + },{ + .tv1 = { 0, 1 }, + .tv2 = { 1, 0 }, + .margin = 999999, + .output = 0, + },{ + .tv1 = { 1, 0 }, + .tv2 = { 1, 999999 }, + .margin = 999999, + .output = 0, + },{ + .tv1 = { 0, 0 }, + .tv2 = { 1, 0 }, + .margin = 999999, + .output = -1, + },{ + .tv1 = { 1, 0 }, + .tv2 = { 2, 0 }, + .margin = 999999, + .output = -1, + },{ + .tv1 = { 10, 0 }, + .tv2 = { 11, 500000 }, + .margin = 1500000, + .output = 0, + },{ + .tv1 = { 8, 500000 }, + .tv2 = { 10, 0 }, + .margin = 1500000, + .output = 0, + },{ + .tv1 = { 10, 0 }, + .tv2 = { 11, 500001 }, + .margin = 1500000, + .output = -1, + },{ + .tv1 = { 8, 499999 }, + .tv2 = { 10, 0 }, + .margin = 1500000, + .output = -1, + },{ + .tv1 = { 1517925358, 999989 }, + .tv2 = { 1517925359, 753 }, + .margin = 2000, + .output = 0, + } + }; + unsigned int i; + + test_begin("timeval_cmp_margin()"); + for (i = 0; i < N_ELEMENTS(tests); i++) { + const struct timeval *tv1 = &tests[i].tv1, *tv2 = &tests[i].tv2; + unsigned int margin = tests[i].margin; + int output = tests[i].output; + + test_assert(timeval_cmp_margin(tv1, tv2, margin) == output); + test_assert(timeval_cmp_margin(tv2, tv1, margin) == -output); + } + test_end(); +} + +static void test_timeval_diff(void) +{ + static const struct timeval input[] = { + { 1, 0 }, { 0, 999999 }, + { 1, 0 }, { 0, 999001 }, + { 1, 1 }, { 0, 999001 }, + { 2, 1 }, { 1, 0 }, + { INT_MAX, 0 }, { INT_MAX-1, 1 } + }; + static int output[] = { + 1, + 999, + 1000, + 1000001, + 999999 + }; + unsigned int i; + long long udiff; + int mdiff; + + test_begin("timeval_diff_*()"); + for (i = 0; i < N_ELEMENTS(input); i += 2) { + udiff = timeval_diff_usecs(&input[i], &input[i+1]); + mdiff = timeval_diff_msecs(&input[i], &input[i+1]); + test_assert(udiff == output[i/2]); + test_assert(mdiff == udiff/1000); + + udiff = timeval_diff_usecs(&input[i+1], &input[i]); + mdiff = timeval_diff_msecs(&input[i+1], &input[i]); + test_assert(udiff == -output[i/2]); + test_assert(mdiff == udiff/1000); + } + test_end(); +} + +static void test_time_to_local_day_start(void) +{ + /* Try this around days when DST changes in some of the more popular + timezones. If that works, everything else probably works too. */ + const struct tm tests[] = { + /* Europe winter -> summer */ + { .tm_year = 2017-1900, .tm_mon = 2, .tm_mday = 26 }, + { .tm_year = 2017-1900, .tm_mon = 2, .tm_mday = 26, + .tm_hour = 23, .tm_min = 59, .tm_sec = 59 }, + /* Europe summer -> winter */ + { .tm_year = 2017-1900, .tm_mon = 9, .tm_mday = 29 }, + { .tm_year = 2017-1900, .tm_mon = 9, .tm_mday = 29, + .tm_hour = 23, .tm_min = 59, .tm_sec = 59 }, + /* USA winter -> summer */ + { .tm_year = 2017-1900, .tm_mon = 2, .tm_mday = 12 }, + { .tm_year = 2017-1900, .tm_mon = 2, .tm_mday = 12, + .tm_hour = 23, .tm_min = 59, .tm_sec = 59 }, + /* USA summer -> winter */ + { .tm_year = 2017-1900, .tm_mon = 10, .tm_mday = 5 }, + { .tm_year = 2017-1900, .tm_mon = 10, .tm_mday = 5, + .tm_hour = 23, .tm_min = 59, .tm_sec = 59 }, + + /* (some of) Australia summer -> winter */ + { .tm_year = 2017-1900, .tm_mon = 3, .tm_mday = 2 }, + { .tm_year = 2017-1900, .tm_mon = 3, .tm_mday = 2, + .tm_hour = 23, .tm_min = 59, .tm_sec = 59 }, + /* (some of) Australia winter -> summer */ + { .tm_year = 2017-1900, .tm_mon = 9, .tm_mday = 1 }, + { .tm_year = 2017-1900, .tm_mon = 9, .tm_mday = 1, + .tm_hour = 23, .tm_min = 59, .tm_sec = 59 }, + }; + const struct tm *tm; + struct tm tm_copy; + time_t t; + + test_begin("time_to_local_day_start()"); + for (unsigned i = 0; i < N_ELEMENTS(tests); i++) { + tm_copy = tests[i]; + tm_copy.tm_isdst = -1; + t = mktime(&tm_copy); + test_assert_idx(t != (time_t)-1, i); + + t = time_to_local_day_start(t); + tm = localtime(&t); + test_assert_idx(tm->tm_year == tests[i].tm_year && + tm->tm_mon == tests[i].tm_mon && + tm->tm_mday == tests[i].tm_mday, i); + test_assert_idx(tm->tm_hour == 0 && tm->tm_min == 0 && + tm->tm_sec == 0, i); + } + test_end(); +} + +static void test_timestamp(const char *ts, int idx) +{ + /* %G:%H:%M:%S */ + const char **t = t_strsplit(ts, ":"); + unsigned len = str_array_length(t); + test_assert_idx(len == 4, idx); + + /* %G - ISO 8601 year */ + test_assert_idx(strlen(t[0]) == 4, idx); + unsigned v = 0; + test_assert_idx(str_to_uint(t[0], &v) == 0, idx); + test_assert_idx(1000 <= v, idx); + test_assert_idx(v <= 3000, idx); + + /* %H - hour from 00 to 23 */ + test_assert_idx(strlen(t[1]) == 2, idx); + test_assert_idx(str_to_uint(t[1], &v) == 0, idx); + test_assert_idx(v <= 23, idx); + + /* %M - minute from 00 to 59 */ + test_assert_idx(strlen(t[2]) == 2, idx); + test_assert_idx(str_to_uint(t[2], &v) == 0, idx); + test_assert_idx(v <= 59, idx); + + /* %S - second from 00 to 60 */ + test_assert_idx(strlen(t[3]) == 2, idx); + test_assert_idx(str_to_uint(t[3], &v) == 0, idx); + test_assert_idx(v <= 60, idx); +} + +#define TS_FMT "%G:%H:%M:%S" +static void test_strftime_now(void) +{ + test_begin("t_strftime and variants now"); + + time_t now = time(NULL); + test_timestamp(t_strftime(TS_FMT, gmtime(&now)), 0); + test_timestamp(t_strfgmtime(TS_FMT, now), 1); + test_timestamp(t_strflocaltime(TS_FMT, now), 2); + + test_end(); +} + +#define RFC2822_FMT "%a, %d %b %Y %T" +static void test_strftime_fixed(void) +{ + test_begin("t_strftime and variants fixed timestamp"); + + time_t ts = 1481222536; + const char *exp = "Thu, 08 Dec 2016 18:42:16"; + test_assert(strcmp(t_strftime(RFC2822_FMT, gmtime(&ts)), exp) == 0); + test_assert(strcmp(t_strfgmtime(RFC2822_FMT, ts), exp) == 0); + + test_end(); +} + +static void test_micro_nanoseconds(void) +{ + uint64_t secs, usecs, nsecs; + + test_begin("i_microseconds() and i_nanoseconds()"); + + secs = time(NULL); + usecs = i_microseconds(); + nsecs = i_nanoseconds(); + + /* Assume max 1 seconds time difference between the calls. That should + be more than enough, while still not failing if there are temporary + hangs when running in heavily loaded systems. */ + test_assert(usecs/1000000 - secs <= 1); + test_assert(nsecs/1000 - usecs <= 1000000); + + test_end(); +} + +static void test_str_to_timeval(void) +{ + struct { + const char *str; + time_t tv_sec; + suseconds_t tv_usec; + } tests[] = { + { "0", 0, 0 }, + { "0.0", 0, 0 }, + { "0.000000", 0, 0 }, + { "0.1", 0, 100000 }, + { "0.100000", 0, 100000 }, + { "0.000001", 0, 1 }, + { "0.100000", 0, 100000 }, + { "2147483647", 2147483647, 0 }, + { "2147483647.999999", 2147483647, 999999 }, + }; + const char *test_failures[] = { + "", + "0.", + "0.0000000", + "1234.-1", + "1234.x", + "x", + "x.100", + }; + struct timeval tv; + + test_begin("str_to_timeval"); + for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) { + test_assert_idx(str_to_timeval(tests[i].str, &tv) == 0, i); + test_assert_idx(tv.tv_sec == tests[i].tv_sec, i); + test_assert_idx(tv.tv_usec == tests[i].tv_usec, i); + } + for (unsigned int i = 0; i < N_ELEMENTS(test_failures); i++) + test_assert_idx(str_to_timeval(test_failures[i], &tv) == -1, i); + test_end(); +} + +void test_time_util(void) +{ + test_timeval_cmp(); + test_timeval_cmp_margin(); + test_timeval_diff(); + test_time_to_local_day_start(); + test_strftime_now(); + test_strftime_fixed(); + test_micro_nanoseconds(); + test_str_to_timeval(); +} |