This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] time/tst-strptime2.c: test full input range -9999 to +9999


strptime's %z specifier parses a string consisting of a sign ('+'
or '-'), two hours digits, and optionally two minutes digits, into
a tm.gmtoff field containing the signed number of seconds the time
zone is offset from UTC time.

The time/tst-strptime2.c file passes a short list of strings through
strptime, validating that either the gmtoff value returned matches
an expected value, or that strptime returns an expected NULL for
invalid strings (for example, when the minutes portion of the string
is outside of the range 00 to 59, or the sign is missing before the
hours digits).

In review of strptime fixes, Carlos O'Donell expressed a wish that
the test function iterate through the entire range of all possible
numeric strings (-9999 to +9999) which could be passed to strptime
%z, and validate the correct response.

Specifically, the test will look for a NULL response from strptime
when:

  * sign ('+' or '-') exists before the first digit (invalid
    format).
  * A sign and no digits are found (invalid format).
  * A sign and one digit are found (invalid format).
  * A sign and three digits are found (invalid format).
  * A sign and four digits (-9999 to +9999) are found but the last
    two digits (minutes) are in the range 60 to 99.

The test will look for a success response from strptime with tm.gmtoff
matching the calculated gmtoff value when:

  * A sign and four digits are found (-9999 to +9999), and the last
    two digits (minutes) are in the range 00 to 59.
  * A sign and two digit strings are found (-99 to +99).

The test's iteration over the possible digit values results in 22218
test runs calculated by the test, run, and passed by strptime:

  * In 12198 of the runs, strptime returns successfully with gmtoff
    matching the expected value.
  * In 10020 of the runs, strptime returns failure (NULL) because the
    string was an invalid format.

2015-08-13  James Perkins  <james@loowit.net>

	* time/tst-strptime2.c (tests): Replace short list of test
	strings for strptime %z specifier with code that calculates
	every combination of digits. Add tests for 0, 1, and 3 digit
	invalid strings, which strptime correctly rejects.

Signed-off-by: James Perkins <james@loowit.net>
---
 time/tst-strptime2.c | 190 +++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 137 insertions(+), 53 deletions(-)

diff --git a/time/tst-strptime2.c b/time/tst-strptime2.c
index 4db8321..8ea8b14 100644
--- a/time/tst-strptime2.c
+++ b/time/tst-strptime2.c
@@ -2,72 +2,156 @@
 #include <stdio.h>
 #include <time.h>
 
+/* Dummy string is used to match strptime's %s specifier.  */
+
+static const char const *dummy_string = "1113472456";
+
+/* buffer_size contains the maximum test string length,
+   including trailing NUL.  */
+
+enum {
+  buffer_size = 20,
+};
 
-static const struct
-{
-  const char *fmt;
-  long int gmtoff;
-} tests[] =
-  {
-    { "1113472456 +1000", 36000 },
-    { "1113472456 -1000", -36000 },
-    { "1113472456 +10", 36000 },
-    { "1113472456 -10", -36000 },
-    { "1113472456 +1030", 37800 },
-    { "1113472456 -1030", -37800 },
-    { "1113472456 +0030", 1800 },
-    { "1113472456 -0030", -1800 },
-    { "1113472456 +1157", 43020 },
-    { "1113472456 +1158", 43080 },
-    { "1113472456 +1159", 43140 },
-    { "1113472456 +1200", 43200 },
-    { "1113472456 -1200", -43200 },
-    { "1113472456 +1201", 43260 },
-    { "1113472456 -1201", -43260 },
-    { "1113472456 +1330", 48600 },
-    { "1113472456 -1330", -48600 },
-    { "1113472456 +1400", 50400 },
-    { "1113472456 +1401", 50460 },
-    { "1113472456 -2459", -89940 },
-    { "1113472456 -2500", -90000 },
-    { "1113472456 +2559", 93540 },
-    { "1113472456 +2600", 93600 },
-    { "1113472456 -99", -356400 },
-    { "1113472456 +99", 356400 },
-    { "1113472456 -9959", -359940 },
-    { "1113472456 +9959", 359940 },
-    { "1113472456 -1060", LONG_MAX },
-    { "1113472456 +1060", LONG_MAX },
-    { "1113472456  1030", LONG_MAX },
-  };
-#define ntests (sizeof (tests) / sizeof (tests[0]))
 
+/*
+   Write a string into the supplied buffer, containing a dummy string,
+   + or - sign, two hour digits, optionally two minutes digits,
+   and trailing NUL.
+
+   Also, calculate and return expected results for this value.  If the
+   input is valid then the expected gmtoffset is returned. If the
+   value is invalid input to strptime, then LONG_MAX is returned.
+   LONG_MAX indicates the expectation that strptime will return NULL;
+   for example, if the number of digits are not correct, or minutes
+   part of the time is outside the valid range of 00 to 59.
+   */
 
 static int
-do_test (void)
+mkbuf (char *buf, int hhmm_signed, size_t ndigits)
 {
-  int result = 0;
+  const int mm_max = 59;
+  int neg = hhmm_signed < 0 ? 1 : 0;
+  unsigned int hhmm = neg ? -hhmm_signed : hhmm_signed;
+  unsigned int hh = hhmm / 100;
+  unsigned int mm = hhmm % 100;
+  char sign = neg ? '-' : '+';
+  int gmtoff_signed = LONG_MAX;
 
-  for (int i = 0; i < ntests; ++i)
+  switch (ndigits)
     {
-      struct tm tm;
+      case 0:
+	sprintf(buf, "%s %c", dummy_string, sign);
+	break;
 
-      if (strptime (tests[i].fmt, "%s %z", &tm) == NULL)
-	{
-	  if (tests[i].gmtoff != LONG_MAX)
-	    {
-	      printf ("round %d: strptime unexpectedly failed\n", i);
-	      result = 1;
-	    }
-	  continue;
-	}
+      case 1:
+      case 2:
+	sprintf(buf, "%s %c%0*u", dummy_string, sign, ndigits, hh);
+	break;
+
+      case 3:
+      case 4:
+	sprintf(buf, "%s %c%0*u", dummy_string, sign, ndigits, hhmm);
+	break;
+
+      default:
+	sprintf(buf, "%s %c%u", dummy_string, sign, hhmm);
+	break;
+
+    }
+
+  if (mm <= mm_max && (ndigits == 2 || ndigits == 4))
+    {
+      int gmtoff = hh * 3600 + mm * 60;
 
-      if (tm.tm_gmtoff != tests[i].gmtoff)
+      gmtoff_signed = neg ? -gmtoff : gmtoff;
+    }
+
+  return gmtoff_signed;
+}
+
+
+/* Using buffer, test strptime against expected gmtoff result.  */
+
+static int
+compare (const char *buf, int gmtoff)
+{
+  int result = 0;
+  struct tm tm;
+
+  if (strptime (buf, "%s %z", &tm) == NULL)
+    {
+      if (gmtoff != LONG_MAX)
 	{
-	  printf ("round %d: tm_gmtoff is %ld\n", i, (long int) tm.tm_gmtoff);
+	  printf ("%s: strptime unexpectedly failed\n", buf);
 	  result = 1;
 	}
     }
+  else if (tm.tm_gmtoff != gmtoff)
+    {
+      printf ("%s: tm_gmtoff is %ld\n", buf, (long int) tm.tm_gmtoff);
+      result = 1;
+    }
+
+  return result;
+}
+
+
+static int
+do_test (void)
+{
+  const int hhmm_min = -9999;
+  const int hhmm_max = 9999;
+  const int hh_min = -99;
+  const int hh_max = 99;
+  int result = 0;
+  int gmtoff;
+  char buf[buffer_size];
+
+  /* Test sign missing, four digits string (invalid format).  */
+
+  sprintf(buf, "%s  1030", dummy_string);
+  gmtoff = LONG_MAX;
+  result |= compare(buf, gmtoff);
+
+  /* Test sign and no digits strings (invalid format).  */
+
+  gmtoff = mkbuf(buf, 0, 0);
+  result |= compare(buf, gmtoff);
+
+  /* Test sign and one digit strings (invalid format).  */
+
+  for (int hh = hh_min / 10; hh <= hh_max / 10; hh++)
+    {
+      gmtoff = mkbuf(buf, hh * 100, 1);
+      result |= compare(buf, gmtoff);
+    }
+
+  /* Test sign and two digits strings (valid format).  */
+
+  for (int hh = hh_min; hh <= hh_max; hh++)
+    {
+      gmtoff = mkbuf(buf, hh * 100, 2);
+      result |= compare(buf, gmtoff);
+    }
+
+  /* Test sign and three digits strings (invalid format).  */
+
+  for (int hhmm = hhmm_min / 10; hhmm <= hhmm_max / 10; hhmm++)
+    {
+      gmtoff = mkbuf(buf, hhmm, 1);
+      result |= compare(buf, gmtoff);
+    }
+
+  /* Test sign and four digits strings. This includes cases
+     where minutes are in the range 0 to 59 (valid), and
+     where minutes are in the range 60 to 99 (invalid).  */
+
+  for (int hhmm = hhmm_min; hhmm <= hhmm_max; hhmm++)
+    {
+      gmtoff = mkbuf(buf, hhmm, 4);
+      result |= compare(buf, gmtoff);
+    }
 
   return result;
 }
-- 
2.5.0


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]