1 /* $OpenBSD: fmt_test.c,v 1.17 2021/06/20 14:09:59 tb Exp $ */
2 
3 /*
4  * Combined tests for fmt_scaled and scan_scaled.
5  * Ian Darwin, January 2001. Public domain.
6  */
7 
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/types.h>
12 #include <errno.h>
13 #include <limits.h>
14 #include <unistd.h>
15 
16 #include <util.h>
17 
18 static int fmt_test(void);
19 static int scan_test(void);
20 
21 static void print_errno(int e);
22 static int assert_int(int testnum, int checknum, int expect, int result);
23 static int assert_errno(int testnum, int checknum, int expect, int result);
24 static int assert_llong(int testnum, int checknum, long long expect, long long result);
25 static int assert_str(int testnum, int checknum, char * expect, char * result);
26 
27 extern char *__progname;
28 static int verbose = 0;
29 
30 __dead static void usage(int stat)
31 {
32 	fprintf(stderr, "usage: %s [-v]\n", __progname);
33 	exit(stat);
34 }
35 
36 int
37 main(int argc, char **argv)
38 {
39 	int i, ch;
40 
41 	while ((ch = getopt(argc, argv, "hv")) != -1) {
42 			switch (ch) {
43 			case 'v':
44 					verbose = 1;
45 					break;
46 			case 'h':
47 					usage(0);
48 			case '?':
49 			default:
50 					usage(1);
51 			}
52 	}
53 	argc -= optind;
54 	argv += optind;
55 
56 	if (verbose)
57 		printf("Starting fmt_test\n");
58 	i = fmt_test();
59 	if (verbose)
60 		printf("Starting scan_test\n");
61 	i += scan_test();
62 	if (i) {
63 		printf("*** %d errors in libutil/fmt_scaled tests ***\n", i);
64 	} else {
65 		if (verbose)
66 			printf("Tests done; no unexpected errors\n");
67 	}
68 	return i;
69 }
70 
71 /************** tests for fmt_scaled *******************/
72 
73 static struct {			/* the test cases */
74 	long long input;
75 	char *expect;
76 	int err;
77 } ddata[] = {
78 	{ 0, "0B", 0 },
79 	{ 1, "1B", 0 },
80 	{ -1, "-1B", 0 },
81 	{ 100, "100B", 0},
82 	{ -100, "-100B", 0},
83 	{ 999, "999B", 0 },
84 	{ 1000, "1000B", 0 },
85 	{ 1023, "1023B", 0 },
86 	{ -1023, "-1023B", 0 },
87 	{ 1024, "1.0K", 0 },
88 	{ 1025, "1.0K", 0 },
89 	{ 1234, "1.2K", 0 },
90 	{ -1234, "-1.2K", 0 },
91 	{ 1484, "1.4K", 0 },		/* rounding boundary, down */
92 	{ 1485, "1.5K", 0 },		/* rounding boundary, up   */
93 	{ -1484, "-1.4K", 0 },		/* rounding boundary, down */
94 	{ -1485, "-1.5K", 0 },		/* rounding boundary, up   */
95 	{ 1536, "1.5K", 0 },
96 	{ 1786, "1.7K", 0 },
97 	{ 1800, "1.8K", 0 },
98 	{ 2000, "2.0K", 0 },
99 	{ 123456, "121K", 0 },
100 	{ 578318, "565K", 0 },
101 	{ 902948, "882K", 0 },
102 	{ 1048576, "1.0M", 0},
103 	{ 1048628, "1.0M", 0},
104 	{ 1049447, "1.0M", 0},
105 	{ -102400, "-100K", 0},
106 	{ -103423, "-101K", 0 },
107 	{ 7299072, "7.0M", 0 },
108 	{ 409478144L, "391M", 0 },
109 	{ -409478144L, "-391M", 0 },
110 	{ 999999999L, "954M", 0 },
111 	{ 1499999999L, "1.4G", 0 },
112 	{ 12475423744LL, "11.6G", 0},
113 	{ 1LL<<61, "2.0E", 0 },
114 	{ 1LL<<62, "4.0E", 0 },
115 	{ 1LL<<63, "", ERANGE },
116 	{ 1099512676352LL, "1.0T", 0}
117 };
118 #	define DDATA_LENGTH (sizeof ddata/sizeof *ddata)
119 
120 static int
121 fmt_test(void)
122 {
123 	unsigned int i, e, errs = 0;
124 	int ret;
125 	char buf[FMT_SCALED_STRSIZE];
126 
127 	for (i = 0; i < DDATA_LENGTH; i++) {
128 		strlcpy(buf, "UNSET", FMT_SCALED_STRSIZE);
129 		errno = 0;
130 		ret = fmt_scaled(ddata[i].input, buf);
131 		e = errno;
132 		if (verbose) {
133 			printf("%lld --> %s (%d)", ddata[i].input, buf, ret);
134 			if (ret == -1)
135 				print_errno(e);
136 			printf("\n");
137 		}
138 		if (ret == -1)
139 			errs += assert_int(i, 1, ret, ddata[i].err == 0 ? 0 : -1);
140 		if (ddata[i].err)
141 			errs += assert_errno(i, 2, ddata[i].err, e);
142 		else
143 			errs += assert_str(i, 3, ddata[i].expect, buf);
144 	}
145 
146 	return errs;
147 }
148 
149 /************** tests for scan_scaled *******************/
150 
151 
152 #define	IMPROBABLE	(-42)
153 
154 struct {					/* the test cases */
155 	char *input;
156 	long long result;
157 	int err;
158 } sdata[] = {
159 	{ "0",		0, 0 },
160 	{ "123",	123, 0 },
161 	{ "1k",		1024, 0 },		/* lower case */
162 	{ "100.944", 100, 0 },	/* should --> 100 (truncates fraction) */
163 	{ "10099",	10099LL, 0 },
164 	{ "1M",		1048576LL, 0 },
165 	{ "1.1M",	1153433LL, 0 },		/* fractions */
166 	{ "1.111111111111111111M",	1165084LL, 0 },		/* fractions */
167 	{ "1.55M",	1625292LL, 0 },	/* fractions */
168 	{ "1.9M",	1992294LL, 0 },		/* fractions */
169 	{ "-2K",	-2048LL, 0 },		/* negatives */
170 	{ "-2.2K",	-2252LL, 0 },	/* neg with fract */
171 	{ "4.5k", 4608, 0 },
172 	{ "3.333755555555t", 3665502936412, 0 },
173 	{ "-3.333755555555t", -3665502936412, 0 },
174 	{ "4.5555555555555555K", 4664, 0 },
175 	{ "4.5555555555555555555K", 4664, 0 },	/* handle enough digits? */
176 	{ "4.555555555555555555555555555555K", 4664, 0 }, /* ignores extra digits? */
177 	{ "1G",		1073741824LL, 0 },
178 	{ "G", 		0, 0 },			/* should == 0G? */
179 	{ "1234567890", 1234567890LL, 0 },	/* should work */
180 	{ "1.5E",	1729382256910270464LL, 0 },		/* big */
181 	{ "32948093840918378473209480483092", 0, ERANGE },  /* too big */
182 	{ "1.5Q",	0, EINVAL },		/* invalid multiplier */
183 	{ "1ab",	0, EINVAL },		/* ditto */
184 	{ "3&",		0, EINVAL },		/* ditto */
185 	{ "5.0e3",	0, EINVAL },	/* digits after */
186 	{ "5.0E3",	0, EINVAL },	/* ditto */
187 	{ "1..0",	0, EINVAL },		/* bad format */
188 	{ "",		0, 0 },			/* boundary */
189 	{ "--1", -1, EINVAL },
190 	{ "++42", -1, EINVAL },
191 	{ "SCALE_OVERFLOW", 0, ERANGE },
192 	{ "SCALE_UNDERFLOW", 0, ERANGE },
193 	{ "LLONG_MAX_K", (LLONG_MAX / 1024) * 1024, 0 },
194 	{ "LLONG_MIN_K", (LLONG_MIN / 1024) * 1024, 0 },
195 	{ "LLONG_MAX", LLONG_MAX, 0 },	/* upper limit */
196 
197 	/*
198 	 * Lower limit is a bit special: because scan_scaled accumulates into a
199 	 * signed long long it can only handle up to the negative value of
200 	 * LLONG_MAX not LLONG_MIN.
201 	 */
202 	{ "NEGATIVE_LLONG_MAX", LLONG_MAX*-1, 0 },	/* lower limit */
203 	{ "LLONG_MIN", 0, ERANGE },	/* can't handle */
204 #if LLONG_MAX == 0x7fffffffffffffffLL
205 	{ "-9223372036854775807", -9223372036854775807, 0 },
206 	{ "9223372036854775807", 9223372036854775807, 0 },
207 	{ "9223372036854775808", 0, ERANGE },
208 	{ "9223372036854775809", 0, ERANGE },
209 #endif
210 #if LLONG_MIN == (-0x7fffffffffffffffLL-1)
211 	{ "-9223372036854775808", 0, ERANGE },
212 	{ "-9223372036854775809", 0, ERANGE },
213 	{ "-9223372036854775810", 0, ERANGE },
214 #endif
215 };
216 #	define SDATA_LENGTH (sizeof sdata/sizeof *sdata)
217 
218 static void
219 print_errno(int e)
220 {
221 	switch(e) {
222 		case EINVAL: printf("EINVAL"); break;
223 		case EDOM:   printf("EDOM"); break;
224 		case ERANGE: printf("ERANGE"); break;
225 		default: printf("errno %d", e);
226 	}
227 }
228 
229 /** Print one result */
230 static void
231 print(char *input, long long result, int ret, int e)
232 {
233 	printf("\"%40s\" --> %lld (%d)", input, result, ret);
234 	if (ret == -1) {
235 		printf(" -- ");
236 		print_errno(e);
237 	}
238 	printf("\n");
239 }
240 
241 static int
242 scan_test(void)
243 {
244 	unsigned int i, errs = 0, e;
245 	int ret;
246 	long long result;
247 	char buf[1024], *input;
248 
249 	for (i = 0; i < SDATA_LENGTH; i++) {
250 		result = IMPROBABLE;
251 
252 		input = sdata[i].input;
253 		/* some magic values for architecture dependent limits */
254 		if (strcmp(input, "LLONG_MAX") == 0) {
255 			snprintf(buf, sizeof buf," %lld", LLONG_MAX);
256 			input = buf;
257 		} else if (strcmp(input, "LLONG_MIN") == 0) {
258 			snprintf(buf, sizeof buf," %lld", LLONG_MIN);
259 			input = buf;
260 		} else if (strcmp(input, "LLONG_MAX_K") == 0) {
261 			snprintf(buf, sizeof buf," %lldK", LLONG_MAX/1024);
262 			input = buf;
263 		} else if (strcmp(input, "LLONG_MIN_K") == 0) {
264 			snprintf(buf, sizeof buf," %lldK", LLONG_MIN/1024);
265 			input = buf;
266 		} else if (strcmp(input, "SCALE_OVERFLOW") == 0) {
267 			snprintf(buf, sizeof buf," %lldK", (LLONG_MAX/1024)+1);
268 			input = buf;
269 		} else if (strcmp(input, "SCALE_UNDERFLOW") == 0) {
270 			snprintf(buf, sizeof buf," %lldK", (LLONG_MIN/1024)-1);
271 			input = buf;
272 		} else if (strcmp(input, "NEGATIVE_LLONG_MAX") == 0) {
273 			snprintf(buf, sizeof buf," %lld", LLONG_MAX*-1);
274 			input = buf;
275 		}
276 		if (verbose && input != sdata[i].input)
277 			printf("expand '%s' -> '%s'\n", sdata[i].input,
278 			    input);
279 
280 		/* printf("Calling scan_scaled(%s, ...)\n", sdata[i].input); */
281 		errno = 0;
282 		ret = scan_scaled(input, &result);
283 		e = errno;	/* protect across printfs &c. */
284 		if (verbose)
285 			print(input, result, ret, e);
286 		if (ret == -1)
287 			errs += assert_int(i, 1, ret, sdata[i].err == 0 ? 0 : -1);
288 		if (sdata[i].err)
289 			errs += assert_errno(i, 2, sdata[i].err, e);
290 		else
291 			errs += assert_llong(i, 3, sdata[i].result, result);
292 	}
293 	return errs;
294 }
295 
296 /************** common testing stuff *******************/
297 
298 static int
299 assert_int(int testnum, int check, int expect, int result)
300 {
301 	if (expect == result)
302 		return 0;
303 	printf("** FAILURE: test %d check %d, expect %d, result %d **\n",
304 		testnum, check, expect, result);
305 	return 1;
306 }
307 
308 static int
309 assert_errno(int testnum, int check, int expect, int result)
310 {
311 	if (expect == result)
312 		return 0;
313 	printf("** FAILURE: test %d check %d, expect ",
314 		testnum, check);
315 	print_errno(expect);
316 	printf(", got ");
317 	print_errno(result);
318 	printf(" **\n");
319 	return 1;
320 }
321 
322 static int
323 assert_llong(int testnum, int check, long long expect, long long result)
324 {
325 	if (expect == result)
326 		return 0;
327 	printf("** FAILURE: test %d check %d, expect %lld, result %lld **\n",
328 		testnum, check, expect, result);
329 	return 1;
330 }
331 
332 static int
333 assert_str(int testnum, int check, char * expect, char * result)
334 {
335 	if (strcmp(expect, result) == 0)
336 		return 0;
337 	printf("** FAILURE: test %d check %d, expect %s, result %s **\n",
338 		testnum, check, expect, result);
339 	return 1;
340 }
341