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