1 /* tst-strptime2 - Test strptime %z timezone offset specifier.  */
2 
3 #include <limits.h>
4 #include <stdbool.h>
5 #include <stdio.h>
6 #include <time.h>
7 #include <libc-diag.h>
8 
9 /* Dummy string is used to match strptime's %s specifier.  */
10 
11 static const char dummy_string[] = "1113472456";
12 
13 /* buffer_size contains the maximum test string length, including
14    trailing NUL.  */
15 
16 enum
17 {
18   buffer_size = 20,
19 };
20 
21 /* Verbose execution, set with --verbose command line option.  */
22 
23 static bool verbose;
24 
25 
26 /* mkbuf - Write a test string for strptime with the specified time
27    value and number of digits into the supplied buffer, and return
28    the expected strptime test result.
29 
30    The test string, buf, is written with the following content:
31      a dummy string matching strptime "%s" format specifier,
32      whitespace matching strptime " " format specifier, and
33      timezone string matching strptime "%z" format specifier.
34 
35    Note that a valid timezone string is either "Z" or contains the
36    following fields:
37      Sign field consisting of a '+' or '-' sign,
38      Hours field in two decimal digits, and
39      optional Minutes field in two decimal digits. Optionally,
40      a ':' is used to seperate hours and minutes.
41 
42    This function may write test strings with minutes values outside
43    the valid range 00-59.  These are invalid strings and useful for
44    testing strptime's rejection of invalid strings.
45 
46    The ndigits parameter is used to limit the number of timezone
47    string digits to be written and may range from 0 to 4.  Note that
48    only 2 and 4 digit strings are valid input to strptime; strings
49    with 0, 1 or 3 digits are invalid and useful for testing strptime's
50    rejection of invalid strings.
51 
52    This function returns the behavior expected of strptime resulting
53    from parsing the the test string.  For valid strings, the function
54    returns the expected tm_gmtoff value.  For invalid strings,
55    LONG_MAX is returned.  LONG_MAX indicates the expectation that
56    strptime will return NULL; for example, if the number of digits
57    are not correct, or minutes part of the time is outside the valid
58    range of 00 to 59.  */
59 
60 static long int
mkbuf(char * buf,bool neg,bool colon,unsigned int hhmm,size_t ndigits)61 mkbuf (char *buf, bool neg, bool colon, unsigned int hhmm, size_t ndigits)
62 {
63   const int mm_max = 59;
64   char sign = neg ? '-' : '+';
65   int i;
66   unsigned int hh = hhmm / 100;
67   unsigned int mm = hhmm % 100;
68   long int expect = LONG_MAX;
69 
70   i = sprintf (buf, "%s %c", dummy_string, sign);
71 #if __GNUC_PREREQ (7, 0)
72   /* GCC issues a warning when it thinks the snprintf buffer may be too short.
73      This test is explicitly using short buffers to force snprintf to truncate
74      the output so we ignore the warnings.  */
75   DIAG_PUSH_NEEDS_COMMENT;
76   DIAG_IGNORE_NEEDS_COMMENT (7.0, "-Wformat-truncation");
77 #endif
78   if (colon)
79     snprintf (buf + i, ndigits + 2, "%02u:%02u", hh, mm);
80   else
81     snprintf (buf + i, ndigits + 1, "%04u", hhmm);
82 #if __GNUC_PREREQ (7, 0)
83   DIAG_POP_NEEDS_COMMENT;
84 #endif
85 
86   if (mm <= mm_max && (ndigits == 2 || ndigits == 4))
87     {
88       long int tm_gmtoff = hh * 3600 + mm * 60;
89 
90       expect = neg ? -tm_gmtoff : tm_gmtoff;
91     }
92 
93   return expect;
94 }
95 
96 
97 /* Write a description of expected or actual test result to stdout.  */
98 
99 static void
describe(bool string_valid,long int tm_gmtoff)100 describe (bool string_valid, long int tm_gmtoff)
101 {
102   if (string_valid)
103     printf ("valid, tm.tm_gmtoff %ld", tm_gmtoff);
104   else
105     printf ("invalid, return value NULL");
106 }
107 
108 
109 /* Using buffer buf, run strptime.  Compare results against expect,
110   the expected result.  Report failures and verbose results to stdout.
111   Update the result counts.  Return 1 if test failed, 0 if passed.  */
112 
113 static int
compare(const char * buf,long int expect,unsigned int * nresult)114 compare (const char *buf, long int expect, unsigned int *nresult)
115 {
116   struct tm tm;
117   char *p;
118   bool test_string_valid;
119   long int test_result;
120   bool fail;
121   int result;
122 
123   p = strptime (buf, "%s %z", &tm);
124   test_string_valid = p != NULL;
125   test_result = test_string_valid ? tm.tm_gmtoff : LONG_MAX;
126   fail = test_result != expect;
127 
128   if (fail || verbose)
129     {
130       bool expect_string_valid = expect != LONG_MAX;
131 
132       printf ("%s: input \"%s\", expected: ", fail ? "FAIL" : "PASS", buf);
133       describe (expect_string_valid, expect);
134 
135       if (fail)
136 	{
137 	  printf (", got: ");
138 	  describe (test_string_valid, test_result);
139 	}
140 
141       printf ("\n");
142     }
143 
144   result = fail ? 1 : 0;
145   nresult[result]++;
146 
147   return result;
148 }
149 
150 
151 static int
do_test(void)152 do_test (void)
153 {
154   char buf[buffer_size];
155   long int expect;
156   int result = 0;
157   /* Number of tests run with passing (index==0) and failing (index==1)
158      results.  */
159   unsigned int nresult[2];
160   unsigned int ndigits;
161   unsigned int step;
162   unsigned int hhmm;
163 
164   nresult[0] = 0;
165   nresult[1] = 0;
166 
167   /* Create and test input string with no sign and four digits input
168      (invalid format).  */
169 
170   sprintf (buf, "%s  1030", dummy_string);
171   expect = LONG_MAX;
172   result |= compare (buf, expect, nresult);
173 
174   /* Create and test input string with "Z" input (valid format).
175      Expect tm_gmtoff of 0.  */
176 
177   sprintf (buf, "%s Z", dummy_string);
178   expect = 0;
179   result |= compare (buf, expect, nresult);
180 
181   /* Create and test input strings with sign and digits:
182      0 digits (invalid format),
183      1 digit (invalid format),
184      2 digits (valid format),
185      3 digits (invalid format),
186      4 digits (valid format if and only if minutes is in range 00-59,
187 	       otherwise invalid).
188      If format is valid, the returned tm_gmtoff is checked.  */
189 
190   for (ndigits = 0, step = 10000; ndigits <= 4; ndigits++, step /= 10)
191     for (hhmm = 0; hhmm <= 9999; hhmm += step)
192       {
193 	/* Test both positive and negative signs.  */
194 
195 	expect = mkbuf (buf, false, false, hhmm, ndigits);
196 	result |= compare (buf, expect, nresult);
197 
198 	expect = mkbuf (buf, true, false, hhmm, ndigits);
199 	result |= compare (buf, expect, nresult);
200 
201 	/* Test with colon as well.  */
202 
203 	if (ndigits >= 3)
204 	  {
205 	    expect = mkbuf (buf, false, true, hhmm, ndigits);
206 	    result |= compare (buf, expect, nresult);
207 
208 	    expect = mkbuf (buf, true, true, hhmm, ndigits);
209 	    result |= compare (buf, expect, nresult);
210 	  }
211       }
212 
213   if (result > 0 || verbose)
214     printf ("%s: %u input strings: %u fail, %u pass\n",
215 	    result > 0 ? "FAIL" : "PASS",
216 	    nresult[1] + nresult[0], nresult[1], nresult[0]);
217 
218   return result;
219 }
220 
221 
222 /* Add a "--verbose" command line option to test-skeleton.c.  */
223 
224 #define OPT_VERBOSE 10000
225 
226 #define CMDLINE_OPTIONS \
227   { "verbose", no_argument, NULL, OPT_VERBOSE, },
228 
229 #define CMDLINE_PROCESS \
230   case OPT_VERBOSE: \
231     verbose = true; \
232     break;
233 
234 #define TEST_FUNCTION do_test ()
235 #include "../test-skeleton.c"
236