xref: /freebsd/contrib/netbsd-tests/lib/libc/gen/t_humanize_number.c (revision f4b37ed0f8b307b1f3f0f630ca725d68f1dff30d)
1 /*	$NetBSD: t_humanize_number.c,v 1.8 2012/03/18 07:14:08 jruoho Exp $	*/
2 
3 /*-
4  * Copyright (c) 2010, 2011 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <atf-c.h>
30 
31 #include <err.h>
32 #include <inttypes.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #ifdef __FreeBSD__
38 #include <libutil.h>
39 #else
40 #include <util.h>
41 #endif
42 
43 const struct hnopts {
44 	size_t ho_len;
45 	int64_t ho_num;
46 	const char *ho_suffix;
47 	int ho_scale;
48 	int ho_flags;
49 	int ho_retval;			/* expected return value */
50 	const char *ho_retstr;		/* expected string in buffer */
51 } hnopts[] = {
52 	/*
53 	 * Rev. 1.6 produces "10.0".
54 	 */
55 	{ 5, 10737418236ULL * 1024, "",
56 	  HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL, 3, "10T" },
57 
58 	{ 5, 10450000, "",
59 	  HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL, 3, "10M" },
60 	{ 5, 10500000, "",		/* just for reference */
61 	  HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL, 3, "10M" },
62 
63 	/*
64 	 * Trailing space.  Rev. 1.7 produces "1 ".
65 	 */
66 	{ 5, 1, "", 0, HN_NOSPACE, 1, "1" },
67 
68 	{ 5, 1, "", 0, 0, 2, "1 " }, /* just for reference */
69 	{ 5, 1, "", 0, HN_B, 3, "1 B" }, /* and more ... */
70 	{ 5, 1, "", 0, HN_DECIMAL, 2, "1 " },
71 	{ 5, 1, "", 0, HN_NOSPACE | HN_B, 2, "1B" },
72 	{ 5, 1, "", 0, HN_B | HN_DECIMAL, 3, "1 B" },
73 	{ 5, 1, "", 0, HN_NOSPACE | HN_B | HN_DECIMAL, 2, "1B" },
74 
75 	/*
76 	 * Space and HN_B.  Rev. 1.7 produces "1B".
77 	 */
78 	{ 5, 1, "", HN_AUTOSCALE, HN_B, 3, "1 B" },
79 	{ 5, 1000, "",			/* just for reference */
80 	  HN_AUTOSCALE, HN_B, 3, "1 K" },
81 
82 	/*
83 	 * Truncated output.  Rev. 1.7 produces "1.0 K".
84 	 */
85 #ifndef __FreeBSD__
86 	{ 6, 1000, "A", HN_AUTOSCALE, HN_DECIMAL, -1, "" },
87 
88 	/*
89 	 * Failure case reported by Greg Troxel <gdt@NetBSD.org>.
90 	 * Rev. 1.11 incorrectly returns 5 with filling the buffer
91 	 * with "1000".
92 	 */
93 	{ 5, 1048258238, "",
94 	  HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL, 4, "1.0G" },
95 	/* Similar case it prints 1000 where it shouldn't */
96 	{ 5, 1023488, "",
97 	  HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL, 4, "1.0M" },
98 #endif
99 	{ 5, 1023999, "",
100 	  HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL, 4, "1.0M" },
101 };
102 
103 struct hnflags {
104 	int hf_flags;
105 	const char *hf_name;
106 };
107 
108 const struct hnflags scale_flags[] = {
109 	{ HN_GETSCALE, "HN_GETSCALE" },
110 	{ HN_AUTOSCALE, "HN_AUTOSCALE" },
111 };
112 const struct hnflags normal_flags[] = {
113 	{ HN_DECIMAL, "HN_DECIMAL" },
114 	{ HN_NOSPACE, "HN_NOSPACE" },
115 	{ HN_B, "HN_B" },
116 	{ HN_DIVISOR_1000, "HN_DIVISOR_1000" },
117 };
118 
119 const char *formatflags(char *, size_t, const struct hnflags *, size_t, int);
120 void	    newline(void);
121 void	    w_printf(const char *, ...) __printflike(1, 2);
122 int	    main(int, char *[]);
123 
124 const char *
125 formatflags(char *buf, size_t buflen, const struct hnflags *hfs,
126     size_t hfslen, int flags)
127 {
128 	const struct hnflags *hf;
129 	char *p = buf;
130 	ssize_t len = buflen;
131 	unsigned int i, found;
132 	int n;
133 
134 	if (flags == 0) {
135 		snprintf(buf, buflen, "0");
136 		return (buf);
137 	}
138 	for (i = found = 0; i < hfslen && flags & ~found; i++) {
139 		hf = &hfs[i];
140 		if (flags & hf->hf_flags) {
141 			found |= hf->hf_flags;
142 			n = snprintf(p, len, "|%s", hf->hf_name);
143 			if (n >= len) {
144 				p = buf;
145 				len = buflen;
146 				/* Print `flags' as number */
147 				goto bad;
148 			}
149 			p += n;
150 			len -= n;
151 		}
152 	}
153 	flags &= ~found;
154 	if (flags)
155 bad:
156 		snprintf(p, len, "|0x%x", flags);
157 	return (*buf == '|' ? buf + 1 : buf);
158 }
159 
160 static int col, bol = 1;
161 void
162 newline(void)
163 {
164 
165 	fprintf(stderr, "\n");
166 	col = 0;
167 	bol = 1;
168 }
169 
170 void
171 w_printf(const char *fmt, ...)
172 {
173 	char buf[80];
174 	va_list ap;
175 	int n;
176 
177 	va_start(ap, fmt);
178 	if (col >= 0) {
179 		n = vsnprintf(buf, sizeof(buf), fmt, ap);
180 		if (n >= (int)sizeof(buf)) {
181 			col = -1;
182 			goto overflow;
183 		} else if (n == 0)
184 			goto out;
185 
186 		if (!bol) {
187 			if (col + n > 75)
188 				fprintf(stderr, "\n    "), col = 4;
189 			else
190 				fprintf(stderr, " "), col++;
191 		}
192 		fprintf(stderr, "%s", buf);
193 		col += n;
194 		bol = 0;
195 	} else {
196 overflow:
197 		vfprintf(stderr, fmt, ap);
198 	}
199 out:
200 	va_end(ap);
201 }
202 
203 ATF_TC(humanize_number_basic);
204 ATF_TC_HEAD(humanize_number_basic, tc)
205 {
206 
207 	atf_tc_set_md_var(tc, "descr", "Test humanize_number(3)");
208 }
209 
210 ATF_TC_BODY(humanize_number_basic, tc)
211 {
212 	char fbuf[128];
213 	const struct hnopts *ho;
214 	char *buf = NULL;
215 	size_t buflen = 0;
216 	unsigned int i;
217 	int rv = 0;
218 
219 	for (i = 0; i < __arraycount(hnopts); i++) {
220 		ho = &hnopts[i];
221 		if (buflen < ho->ho_len) {
222 			buflen = ho->ho_len;
223 			buf = realloc(buf, buflen);
224 			if (buf == NULL)
225 				atf_tc_fail("realloc(..., %zu) failed", buflen);
226 		}
227 
228 		rv = humanize_number(buf, ho->ho_len, ho->ho_num,
229 		    ho->ho_suffix, ho->ho_scale, ho->ho_flags);
230 
231 		if (rv == ho->ho_retval &&
232 		    (rv == -1 || strcmp(buf, ho->ho_retstr) == 0))
233 			continue;
234 
235 		w_printf("humanize_number(\"%s\", %zu, %" PRId64 ",",
236 		    ho->ho_retstr, ho->ho_len, ho->ho_num);
237 		w_printf("\"%s\",", ho->ho_suffix);
238 		w_printf("%s,", formatflags(fbuf, sizeof(fbuf), scale_flags,
239 		    sizeof(scale_flags) / sizeof(scale_flags[0]),
240 		    ho->ho_scale));
241 		w_printf("%s)", formatflags(fbuf, sizeof(fbuf), normal_flags,
242 		    sizeof(normal_flags) / sizeof(normal_flags[0]),
243 		    ho->ho_flags));
244 		w_printf("= %d,", ho->ho_retval);
245 		w_printf("but got");
246 		w_printf("%d/[%s]", rv, rv == -1 ? "" : buf);
247 		newline();
248 		atf_tc_fail_nonfatal("Failed for table entry %d", i);
249 	}
250 }
251 
252 ATF_TC(humanize_number_big);
253 ATF_TC_HEAD(humanize_number_big, tc)
254 {
255 
256 	atf_tc_set_md_var(tc, "descr", "Test humanize "
257 	    "big numbers (PR lib/44097)");
258 }
259 
260 ATF_TC_BODY(humanize_number_big, tc)
261 {
262 	char buf[1024];
263 	int rv;
264 
265 	/*
266 	 * Seems to work.
267 	 */
268 	(void)memset(buf, 0, sizeof(buf));
269 
270 	rv = humanize_number(buf, 10, 10000, "", HN_AUTOSCALE, HN_NOSPACE);
271 
272 	ATF_REQUIRE(rv != -1);
273 	ATF_CHECK_STREQ(buf, "10000");
274 
275 	/*
276 	 * A bogus value with large number.
277 	 */
278 	(void)memset(buf, 0, sizeof(buf));
279 
280 	rv = humanize_number(buf, 10, INT64_MAX, "", HN_AUTOSCALE, HN_NOSPACE);
281 
282 	ATF_REQUIRE(rv != -1);
283 	ATF_REQUIRE(strcmp(buf, "0") != 0);
284 
285 	/*
286 	 * Large buffer with HN_AUTOSCALE. Entirely bogus.
287 	 */
288 	(void)memset(buf, 0, sizeof(buf));
289 
290 	rv = humanize_number(buf, sizeof(buf), 10000, "",
291 	    HN_AUTOSCALE, HN_NOSPACE);
292 
293 	ATF_REQUIRE(rv != -1);
294 	ATF_REQUIRE(strcmp(buf, "0%d%s%d%s%s%s") != 0);
295 
296 	/*
297 	 * Tight buffer.
298 	 *
299 	 * The man page says that len must be at least 4.
300 	 * 3 works, but anything less that will not. This
301 	 * is because baselen starts with 2 for positive
302 	 * numbers.
303 	 */
304 	(void)memset(buf, 0, sizeof(buf));
305 
306 	rv = humanize_number(buf, 3, 1, "", HN_AUTOSCALE, HN_NOSPACE);
307 
308 	ATF_REQUIRE(rv != -1);
309 }
310 
311 ATF_TP_ADD_TCS(tp)
312 {
313 
314 	ATF_TP_ADD_TC(tp, humanize_number_basic);
315 	ATF_TP_ADD_TC(tp, humanize_number_big);
316 
317 	return atf_no_error();
318 }
319