[PATCH] test: Improve tests for the date/time parser module

Michal Sojka sojkam1 at fel.cvut.cz
Tue Sep 25 05:05:27 PDT 2012


This patch reworks date/time parser library test program to make it
easier to to write the actual tests. It also modifies the notmuch test
script and adds several new tests to it.

The INPUT file for the test contains both the dates to be parsed as well
as the "expected" results. The test program outputs the results in the
same format and replaces expected results with real results. Currently,
the "expected" results in the INPUT file correspond to the real results,
so the test passes. Some results are, however, different from what I
would expect - this is mentioned in the comments after '#'.

This patch applies on top of Jani's patchset.
---
It can be seen that there are several errors and unexpected results.
As I've already written, I'm not sure that the approach taken by this
library is the right one. I tend to agree with mina86, that using a
more systematic approach (such as bison) would be beneficial.

This is however not to say to throw this patchset away. Either Jani
will be able to fix all the corner cases. Or we can work together to
develop a better solution - add support for ranges to the bison
parser.

-Michal

diff --git a/test/Makefile.local b/test/Makefile.local
index 9ae130a..b9105c7 100644
--- a/test/Makefile.local
+++ b/test/Makefile.local
@@ -20,7 +20,7 @@ $(dir)/symbol-test: $(dir)/symbol-test.o
 	$(call quiet,CXX) $^ -o $@ -Llib -lnotmuch $(XAPIAN_LDFLAGS)
 
 $(dir)/parse-time: $(dir)/parse-time.o parse-time-string/parse-time-string.o
-	$(call quiet,CC) $^ -o $@
+	$(call quiet,CC) $^ -o $@ -lrt
 
 .PHONY: test check
 
diff --git a/test/parse-time-string b/test/parse-time-string
index 34b80d7..265437c 100755
--- a/test/parse-time-string
+++ b/test/parse-time-string
@@ -14,13 +14,48 @@ _parse_time ()
     ${TEST_DIRECTORY}/parse-time --format=%s "$*"
 }
 
-test_begin_subtest "date(1) default format without TZ code"
-test_expect_equal "$(_parse_time Fri Aug 3 23:06:06 2012)" "$(_date Fri Aug 3 23:06:06 2012)"
+test_begin_subtest "Date parser tests"
+cat <<EOF > INPUT
+now          -> Tue Jan 11 11:11:00 +0000 2011
+2010-1-1     -> parse_time_string() error: 5
+Jan 2        -> Sat Jan 02 11:11:00 +0000 2010   # Why 2010?
+Mon          -> Mon Jan 10 11:11:00 +0000 2011
+last Friday  -> parse_time_string() error: 4
+2 hours ago  -> parse_time_string() error: 1
+last month   -> Sat Dec 11 11:11:00 +0000 2010
+month ago    -> parse_time_string() error: 1
+8am          -> Tue Jan 11 08:00:00 +0000 2011
+9:15         -> Tue Jan 11 09:15:00 +0000 2011
+12:34        -> Tue Jan 11 12:34:00 +0000 2011
+monday       -> Mon Jan 10 11:11:00 +0000 2011
+yesterday    -> Mon Jan 10 11:11:00 +0000 2011
+tomorrow     -> parse_time_string() error: 1
+             -> Tue Jan 11 11:11:00 +0000 2011 # Shouldn't empty string return an error???
 
-test_begin_subtest "date(1) --rfc-2822 format"
-test_expect_equal "$(_parse_time Fri, 03 Aug 2012 23:07:46 +0100)" "$(_date Fri, 03 Aug 2012 23:07:46 +0100)"
+Aug 3 23:06:06 2012             -> Fri Aug 03 23:06:06 +0000 2012 # date(1) default format without TZ code
+Fri, 03 Aug 2012 23:07:46 +0100 -> Fri Aug 03 22:07:46 +0000 2012 # rfc-2822
+2012-08-03 23:09:37+03:00       -> Fri Aug 03 20:09:37 +0000 2012 # rfc-3339 seconds
 
-test_begin_subtest "date(1) --rfc=3339=seconds format"
-test_expect_equal "$(_parse_time 2012-08-03 23:09:37+03:00)" "$(_date 2012-08-03 23:09:37+03:00)"
+10s           -> Tue Jan 11 11:10:50 +0000 2011
+19701223s     -> Wed Dec 23 11:10:59 +0000 1970 # Surprising - number is parsed as date and 's' as '1 second'
+19701223      -> Wed Dec 23 11:11:00 +0000 1970
+
+19701223 +0100 -> Wed Dec 23 11:11:00 +0000 1970 # Timezone is ignored without an error
+
+today ^-> Wed Jan 12 00:00:00 +0000 2011 # This should be 11 23:59:59
+today v-> Tue Jan 11 00:00:00 +0000 2011
+
+thisweek ^-> Sun Jan 16 00:00:00 +0000 2011  # This should be Sunday 23:59:59
+thisweek v-> Sun Jan 09 00:00:00 +0000 2011  # This should be Monday 00:00:00
+
+two months ago-> parse_time_string() error: 1 # Comments in the code suggest that this is supported
+two months -> Thu Nov 11 11:11:00 +0000 2010
+
+1348569850 -> parse_time_string() error: 4 # Seconds since epoch not yet supported? Backward compatibility in notmuch???
+10 -> parse_time_string() error: 4 # Seconds since epoch?
+EOF
+
+${TEST_DIRECTORY}/parse-time --now="Tue Jan 11 11:11:00 +0000 2011" < INPUT > OUTPUT
+test_expect_equal_file INPUT OUTPUT
 
 test_done
diff --git a/test/parse-time.c b/test/parse-time.c
index b4de76b..0415f49 100644
--- a/test/parse-time.c
+++ b/test/parse-time.c
@@ -18,59 +18,47 @@
  * Author: Jani Nikula <jani at nikula.org>
  */
 
+
+#define _XOPEN_SOURCE 500       /* for strptime() and snprintf() */
 #include <getopt.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 
 #include "parse-time-string.h"
 
-/*
- * concat argv[start]...argv[end - 1], separating them by a single
- * space, to a malloced string
- */
-static char *
-concat_args (int start, int end, char *argv[])
-{
-    int i;
-    size_t len = 1;
-    char *p;
-
-    for (i = start; i < end; i++)
-	len += strlen (argv[i]) + 1;
-
-    p = malloc (len);
-    if (!p)
-	return NULL;
-
-    *p = 0;
-
-    for (i = start; i < end; i++) {
-	if (i != start)
-	    strcat (p, " ");
-	strcat (p, argv[i]);
-    }
-
-    return p;
-}
-
 #define DEFAULT_FORMAT "%a %b %d %T %z %Y"
 
 static void
 usage (const char *name)
 {
-    printf ("Usage: %s [options ...] <date/time>\n\n", name);
+    printf ("Usage: %s [options ...]\n\n", name);
     printf (
-	"Parse <date/time> and display it in given format.\n\n"
-	"  -f, --format=FMT output format, FMT according to strftime(3)\n"
-	"                   (default: \"%s\")\n"
-	"  -n, --now=N      use N seconds since epoch as now (default: now)\n"
-	"  -u, --up         round result up (default: no rounding)\n"
-	"  -d, --down       round result down (default: no rounding)\n"
-	"  -h, --help       print this help\n",
+	"Parse date/time read from stdin and display it in given format.\n\n"
+	"  -f, --format=FMT output format for dates and input format for --now,\n"
+        "                   FMT according to strftime(3) (default: \"%s\")\n"
+	"  -n, --now=N      reference date in FMT (default: now)\n"
+	"  -h, --help       print this help\n"
+	"\n"
+	"stdin should contain one date/time per line in the following format:\n"
+	"  <date/time> [ <arrow> [ comment ] ]\n"
+	"where <arrow> determines the operation performed on the <date/time>.\n"
+	"It can be one of '->', '^->', 'v->' meaning convert, convert and round\n"
+	"up, convert and round down, respectively.\n",
 	DEFAULT_FORMAT);
 }
 
+static const char *
+get_round_str (int round)
+{
+    switch (round) {
+    case PARSE_TIME_ROUND_UP:   return "^";
+    case PARSE_TIME_ROUND_DOWN: return "v";
+    default:			return "";
+    }
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -79,14 +67,10 @@ main (int argc, char *argv[])
     time_t result;
     time_t now;
     time_t *nowp = NULL;
-    char *argstr;
     int round = PARSE_TIME_NO_ROUND;
-    char buf[1024];
     const char *format = DEFAULT_FORMAT;
     struct option options[] = {
 	{ "help",	no_argument,		NULL,	'h' },
-	{ "up",		no_argument,		NULL,	'u' },
-	{ "down",	no_argument,		NULL,	'd' },
 	{ "format",	required_argument,	NULL,	'f' },
 	{ "now",	required_argument,	NULL,	'n' },
 	{ NULL, 0, NULL, 0 },
@@ -111,8 +95,13 @@ main (int argc, char *argv[])
 	    round = PARSE_TIME_ROUND_DOWN;
 	    break;
 	case 'n':
-	    /* specify now in seconds since epoch */
-	    now = (time_t) strtol (optarg, NULL, 10);
+	    memset (&tm, 0, sizeof (tm));
+	    char *parsed = strptime (optarg, format, &tm);
+	    if (!parsed) {
+		fprintf (stderr, "Cannot parse reference date: %s\n", optarg);
+		return 1;
+	    }
+	    now = mktime (&tm);
 	    if (now >= (time_t) 0)
 		nowp = &now;
 	    break;
@@ -124,22 +113,47 @@ main (int argc, char *argv[])
 	}
     }
 
-    argstr = concat_args (optind, argc, argv);
-    if (!argstr)
-	return 1;
-
-    r = parse_time_string (argstr, &result, nowp, round);
-
-    free (argstr);
-
-    if (r)
-	return 1;
-
-    if (!localtime_r (&result, &tm))
-	return 1;
-
-    strftime (buf, sizeof (buf), format, &tm);
-    printf ("%s\n", buf);
+    char input[BUFSIZ];
+    while (fgets (input, BUFSIZ, stdin) && input[0]) {
+	if (input[0] == '\n') {
+	    printf ("\n");
+	    continue;
+	}
+	char *arrow;
+	char *comment = strrchr (input, '#');
+	arrow = strstr (input, "->");
+	round = PARSE_TIME_NO_ROUND;
+	if (arrow > input) {
+	    switch (arrow[-1]) {
+	    case '^': round = PARSE_TIME_ROUND_UP; arrow--; break;
+	    case 'v': round = PARSE_TIME_ROUND_DOWN; arrow--; break;
+	    default: break;
+	    }
+	}
+	if (arrow)
+	    *arrow = 0;
+	else
+	    arrow = input + strlen (input); /* XXX: comment is not handled */
+	while (arrow > input && arrow[-1] == '\n')
+	    arrow--;
+	*arrow-- = 0;
+
+	r = parse_time_string (input, &result, nowp, round);
+	char resstr[BUFSIZ];
+	if (r)
+	    snprintf (resstr, sizeof(resstr), "parse_time_string() error: %d", r);
+	else if (!localtime_r (&result, &tm))
+	    snprintf (resstr, sizeof(resstr), "localtime(result) error");
+	else
+	    strftime (resstr, sizeof (resstr), format, &tm);
+
+	char buf[BUFSIZ];
+	snprintf (buf, sizeof(buf), "%s%s-> %s", input, get_round_str (round), resstr);
+	if (!comment)
+	    printf ("%s\n", buf);
+	else
+	    printf ("%-*s%s", (int)(comment - input), buf, comment);
+    }
 
     return 0;
 }


More information about the notmuch mailing list