xref: /freebsd/lib/libutil/tests/humanize_number_test.c (revision af23369a6deaaeb612ab266eb88b8bb8d560c322)
1 /*-
2  * Copyright 2012 Clifton Royston
3  * Copyright 2013 John-Mark Gurney
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD$
28  *
29  */
30 
31 #include <sys/param.h>
32 #include <inttypes.h>
33 #include <libutil.h>
34 #include <limits.h>
35 #include <math.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 
41 #define	MAX_STR_FLAGS_RESULT	80
42 #define MAX_INT_STR_DIGITS	12
43 
44 static const int64_t halfExabyte = (int64_t)500*1000*1000*1000*1000*1000L;
45 
46 static struct {
47 	int retval;
48 	const char *res;
49 	int64_t num;
50 	int flags;
51 	int scale;
52 	size_t buflen;
53 } test_args[] = {
54 	/* tests 0-13 test 1000 suffixes */
55 	{ 2, "0 ", (int64_t)0L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
56 	{ 3, "1 k", (int64_t)500L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
57 	{ 3, "1 M", (int64_t)500*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
58 	{ 3, "1 G", (int64_t)500*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
59 	{ 3, "1 T", (int64_t)500*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
60 	{ 3, "1 P", (int64_t)500*1000*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
61 	{ 3, "1 E", (int64_t)500*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
62 	{ 2, "1 ", (int64_t)1L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
63 	{ 3, "2 k", (int64_t)1500L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
64 	{ 3, "2 M", (int64_t)1500*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
65 	{ 3, "2 G", (int64_t)1500*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
66 	{ 3, "2 T", (int64_t)1500*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
67 	{ 3, "2 P", (int64_t)1500*1000*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
68 	{ 3, "2 E", (int64_t)1500*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
69 
70 	/* tests 14-27 test 1024 suffixes */
71 	{ 2, "0 ", (int64_t)0L, 0, HN_AUTOSCALE, 4 },
72 	{ 3, "1 K", (int64_t)512L, 0, HN_AUTOSCALE, 4 },
73 	{ 3, "1 M", (int64_t)512*1024L, 0, HN_AUTOSCALE, 4 },
74 	{ 3, "1 G", (int64_t)512*1024*1024L, 0, HN_AUTOSCALE, 4 },
75 	{ 3, "1 T", (int64_t)512*1024*1024*1024L, 0, HN_AUTOSCALE, 4 },
76 	{ 3, "1 P", (int64_t)512*1024*1024*1024*1024L, 0, HN_AUTOSCALE, 4 },
77 	{ 3, "1 E", (int64_t)512*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE, 4 },
78 	{ 2, "1 ", (int64_t)1L, 0, HN_AUTOSCALE, 4 },
79 	{ 3, "2 K", (int64_t)1536L, 0, HN_AUTOSCALE, 4 },
80 	{ 3, "2 M", (int64_t)1536*1024L, 0, HN_AUTOSCALE, 4 },
81 	{ 3, "2 G", (int64_t)1536*1024*1024L, 0, HN_AUTOSCALE, 4 },
82 	{ 3, "2 T", (int64_t)1536*1024*1024*1024L, 0, HN_AUTOSCALE, 4 },
83 	{ 3, "2 P", (int64_t)1536*1024*1024*1024*1024L, 0, HN_AUTOSCALE, 4 },
84 	{ 3, "2 E", (int64_t)1536*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE, 4 },
85 
86 	/* tests 28-37 test rounding */
87 	{ 3, "0 M", (int64_t)500*1000L-1, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
88 	{ 3, "1 M", (int64_t)500*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
89 	{ 3, "1 M", (int64_t)1000*1000L + 500*1000L-1, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
90 	{ 3, "2 M", (int64_t)1000*1000L + 500*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
91 	{ 3, "0 K", (int64_t)512L-1, 0, HN_AUTOSCALE, 4 },
92 	{ 3, "1 K", (int64_t)512L, 0, HN_AUTOSCALE, 4 },
93 	{ 3, "0 M", (int64_t)512*1024L-1, 0, HN_AUTOSCALE, 4 },
94 	{ 3, "1 M", (int64_t)512*1024L, 0, HN_AUTOSCALE, 4 },
95 	{ 3, "1 M", (int64_t)1024*1024L + 512*1024L-1, 0, HN_AUTOSCALE, 4 },
96 	{ 3, "2 M", (int64_t)1024*1024L + 512*1024L, 0, HN_AUTOSCALE, 4 },
97 
98 	/* tests 38-61 test specific scale factors with 1000 divisor */
99 	{ 3, "0 k", (int64_t)0L, HN_DIVISOR_1000, 1, 4 },
100 	{ 3, "1 k", (int64_t)500L, HN_DIVISOR_1000, 1, 4 },
101 	{ 3, "0 M", (int64_t)500L, HN_DIVISOR_1000, 2, 4 },
102 	{ 3, "1 M", (int64_t)500*1000L, HN_DIVISOR_1000, 2, 4 },
103 	{ 3, "0 G", (int64_t)500*1000L, HN_DIVISOR_1000, 3, 4 },
104 	{ 3, "1 G", (int64_t)500*1000*1000L, HN_DIVISOR_1000, 3, 4 },
105 	{ 3, "0 T", (int64_t)500*1000*1000L, HN_DIVISOR_1000, 4, 4 },
106 	{ 3, "1 T", (int64_t)500*1000*1000*1000L, HN_DIVISOR_1000, 4, 4 },
107 	{ 3, "0 P", (int64_t)500*1000*1000*1000L, HN_DIVISOR_1000, 5, 4 },
108 	{ 3, "1 P", (int64_t)500*1000*1000*1000*1000L, HN_DIVISOR_1000, 5, 4 },
109 	{ 3, "0 E", (int64_t)500*1000*1000*1000*1000L, HN_DIVISOR_1000, 6, 4 },
110 	{ 3, "1 E", (int64_t)500*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, 6, 4 },
111 	{ 3, "0 k", (int64_t)1L, HN_DIVISOR_1000, 1, 4 },
112 	{ 3, "2 k", (int64_t)1500L, HN_DIVISOR_1000, 1, 4 },
113 	{ 3, "0 M", (int64_t)1500L, HN_DIVISOR_1000, 2, 4 },
114 	{ 3, "2 M", (int64_t)1500*1000L, HN_DIVISOR_1000, 2, 4 },
115 	{ 3, "0 G", (int64_t)1500*1000L, HN_DIVISOR_1000, 3, 4 },
116 	{ 3, "2 G", (int64_t)1500*1000*1000L, HN_DIVISOR_1000, 3, 4 },
117 	{ 3, "0 T", (int64_t)1500*1000*1000L, HN_DIVISOR_1000, 4, 4 },
118 	{ 3, "2 T", (int64_t)1500*1000*1000*1000L, HN_DIVISOR_1000, 4, 4 },
119 	{ 3, "0 P", (int64_t)1500*1000*1000*1000L, HN_DIVISOR_1000, 5, 4 },
120 	{ 3, "2 P", (int64_t)1500*1000*1000*1000*1000L, HN_DIVISOR_1000, 5, 4 },
121 	{ 3, "0 E", (int64_t)1500*1000*1000*1000*1000L, HN_DIVISOR_1000, 6, 4 },
122 	{ 3, "2 E", (int64_t)1500*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, 6, 4 },
123 
124 	/* tests 62-85 test specific scale factors with 1024 divisor */
125 	{ 3, "0 K", (int64_t)0L, 0, 1, 4 },
126 	{ 3, "1 K", (int64_t)512L, 0, 1, 4 },
127 	{ 3, "0 M", (int64_t)512L, 0, 2, 4 },
128 	{ 3, "1 M", (int64_t)512*1024L, 0, 2, 4 },
129 	{ 3, "0 G", (int64_t)512*1024L, 0, 3, 4 },
130 	{ 3, "1 G", (int64_t)512*1024*1024L, 0, 3, 4 },
131 	{ 3, "0 T", (int64_t)512*1024*1024L, 0, 4, 4 },
132 	{ 3, "1 T", (int64_t)512*1024*1024*1024L, 0, 4, 4 },
133 	{ 3, "0 P", (int64_t)512*1024*1024*1024L, 0, 5, 4 },
134 	{ 3, "1 P", (int64_t)512*1024*1024*1024*1024L, 0, 5, 4 },
135 	{ 3, "0 E", (int64_t)512*1024*1024*1024*1024L, 0, 6, 4 },
136 	{ 3, "1 E", (int64_t)512*1024*1024*1024*1024*1024L, 0, 6, 4 },
137 	{ 3, "0 K", (int64_t)1L, 0, 1, 4 },
138 	{ 3, "2 K", (int64_t)1536L, 0, 1, 4 },
139 	{ 3, "0 M", (int64_t)1536L, 0, 2, 4 },
140 	{ 3, "2 M", (int64_t)1536*1024L, 0, 2, 4 },
141 	{ 3, "0 G", (int64_t)1536*1024L, 0, 3, 4 },
142 	{ 3, "2 G", (int64_t)1536*1024*1024L, 0, 3, 4 },
143 	{ 3, "0 T", (int64_t)1536*1024*1024L, 0, 4, 4 },
144 	{ 3, "2 T", (int64_t)1536*1024*1024*1024L, 0, 4, 4 },
145 	{ 3, "0 P", (int64_t)1536*1024*1024*1024L, 0, 5, 4 },
146 	{ 3, "2 P", (int64_t)1536*1024*1024*1024*1024L, 0, 5, 4 },
147 	{ 3, "0 E", (int64_t)1536*1024*1024*1024*1024L, 0, 6, 4 },
148 	{ 3, "2 E", (int64_t)1536*1024*1024*1024*1024*1024L, 0, 6, 4 },
149 
150 	/* tests 86-99 test invalid specific scale values of < 0 or >= 7 with
151 	and without HN_DIVISOR_1000 set */
152 	/*  all should return errors with new code; with old, the latter 3
153 	are instead processed as if having AUTOSCALE and/or GETSCALE set */
154 	{ -1, "", (int64_t)1L, 0, 7, 4 },
155 	{ -1, "", (int64_t)1L, HN_DIVISOR_1000, 7, 4 },
156 	{ -1, "", (int64_t)1L, 0, 1000, 4 },
157 	{ -1, "", (int64_t)1L, HN_DIVISOR_1000, 1000, 4 },
158 	{ -1, "", (int64_t)0L, 0, 1000*1000, 4 },
159 	{ -1, "", (int64_t)0L, HN_DIVISOR_1000, 1000*1000, 4 },
160 	{ -1, "", (int64_t)0L, 0, INT_MAX, 4 },
161 	{ -1, "", (int64_t)0L, HN_DIVISOR_1000, INT_MAX, 4 },
162 
163 	/* Negative scale values are not handled well
164 	 by the existing library routine - should report as error */
165 	/*  all should return errors with new code, fail assertion with old */
166 
167 	{ -1, "", (int64_t)1L, 0, -1, 4 },
168 	{ -1, "", (int64_t)1L, HN_DIVISOR_1000, -1, 4 },
169 	{ -1, "", (int64_t)1L, 0, -1000, 4 },
170 	{ -1, "", (int64_t)1L, HN_DIVISOR_1000, -1000, 4 },
171 
172 	/* __INT_MIN doesn't print properly, skipped. */
173 
174 	{ -1, "", (int64_t)1L, 0, -__INT_MAX, 4 },
175 	{ -1, "", (int64_t)1L, HN_DIVISOR_1000, -__INT_MAX, 4 },
176 
177 
178 	/* tests for scale == 0, without autoscale */
179 	/* tests 100-114 test scale 0 with 1000 divisor - print first N digits */
180 	{ 2, "0 ", (int64_t)0L, HN_DIVISOR_1000, 0, 4 },
181 	{ 2, "1 ", (int64_t)1L, HN_DIVISOR_1000, 0, 4 },
182 	{ 3, "10 ", (int64_t)10L, HN_DIVISOR_1000, 0, 4 },
183 	{ 3, "0 M", (int64_t)150L, HN_DIVISOR_1000, HN_NOSPACE, 4 },
184 	{ 3, "0 M", (int64_t)500L, HN_DIVISOR_1000, HN_NOSPACE, 4 },
185 	{ 3, "0 M", (int64_t)999L, HN_DIVISOR_1000, HN_NOSPACE, 4 },
186 	{ 4, "150", (int64_t)150L, HN_DIVISOR_1000, 0, 4 },
187 	{ 4, "500", (int64_t)500L, HN_DIVISOR_1000, 0, 4 },
188 	{ 4, "999", (int64_t)999L, HN_DIVISOR_1000, 0, 4 },
189 	{ 5, "100", (int64_t)1000L, HN_DIVISOR_1000, 0, 4 },
190 	{ 5, "150", (int64_t)1500L, HN_DIVISOR_1000, 0, 4 },
191 	{ 7, "500", (int64_t)500*1000L, HN_DIVISOR_1000, 0, 4 },
192 	{ 8, "150", (int64_t)1500*1000L, HN_DIVISOR_1000, 0, 4 },
193 	{ 10, "500", (int64_t)500*1000*1000L, HN_DIVISOR_1000, 0, 4 },
194 	{ 11, "150", (int64_t)1500*1000*1000L, HN_DIVISOR_1000, 0, 4 },
195 
196 	/* tests 115-126 test scale 0 with 1024 divisor - print first N digits */
197 	{ 2, "0 ", (int64_t)0L, 0, 0, 4 },
198 	{ 2, "1 ", (int64_t)1L, 0, 0, 4 },
199 	{ 3, "10 ", (int64_t)10L, 0, 0, 4 },
200 	{ 4, "150", (int64_t)150L, 0, 0, 4 },
201 	{ 4, "500", (int64_t)500L, 0, 0, 4 },
202 	{ 4, "999", (int64_t)999L, 0, 0, 4 },
203 	{ 5, "100", (int64_t)1000L, 0, 0, 4 },
204 	{ 5, "150", (int64_t)1500L, 0, 0, 4 },
205 	{ 7, "500", (int64_t)500*1000L, 0, 0, 4 },
206 	{ 8, "150", (int64_t)1500*1000L, 0, 0, 4 },
207 	{ 10, "500", (int64_t)500*1000*1000L, 0, 0, 4 },
208 	{ 11, "150", (int64_t)1500*1000*1000L, 0, 0, 4 },
209 
210 	/* Test case for rounding of edge numbers around 999.5+, see PR224498.
211 	 * Require buflen >= 5 */
212 	{ 4, "1.0M", (int64_t)1023500, HN_DECIMAL|HN_B|HN_NOSPACE, HN_AUTOSCALE, 5 },
213 
214 	/* Test boundary cases for very large positive/negative number formatting */
215 	/* Explicit scale, divisor 1024 */
216 
217 	/* Requires buflen >= 6 */
218 	{ 3, "8 E",   INT64_MAX, 0, 6, 6 },
219 	{ 4, "-8 E", -INT64_MAX, 0, 6, 6 },
220 	{ 3, "0 E", (int64_t)92*1024*1024*1024*1024*1024L, 0, 6, 6 },
221 	{ 3, "0 E", -(int64_t)92*1024*1024*1024*1024*1024L, 0, 6, 6 },
222 	{ 3, "0 E", (int64_t)82*1024*1024*1024*1024*1024L, 0, 6, 6 },
223 	{ 3, "0 E", -(int64_t)82*1024*1024*1024*1024*1024L, 0, 6, 6 },
224 	{ 3, "0 E", (int64_t)81*1024*1024*1024*1024*1024L, 0, 6, 6 },
225 	{ 3, "0 E", -(int64_t)81*1024*1024*1024*1024*1024L, 0, 6, 6 },
226 	{ 4, "92 P", (int64_t)92*1024*1024*1024*1024*1024L, 0, 5, 6 },
227 	{ 5, "-92 P", -(int64_t)92*1024*1024*1024*1024*1024L, 0, 5, 6 },
228 	{ 4, "82 P", (int64_t)82*1024*1024*1024*1024*1024L, 0, 5, 6 },
229 	{ 5, "-82 P", -(int64_t)82*1024*1024*1024*1024*1024L, 0, 5, 6 },
230 	{ 4, "81 P", (int64_t)81*1024*1024*1024*1024*1024L, 0, 5, 6 },
231 	{ 5, "-81 P", -(int64_t)81*1024*1024*1024*1024*1024L, 0, 5, 6 },
232 
233 	/* Explicit scale, divisor 1000 */
234 	{ 3, "9 E",   INT64_MAX, HN_DIVISOR_1000, 6, 6 },
235 	{ 4, "-9 E", -INT64_MAX, HN_DIVISOR_1000,  6, 6 },
236 	{ 3, "0 E", (int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000,  6, 6 },
237 	{ 3, "0 E", -(int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000,  6, 6 },
238 	{ 3, "0 E", (int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000,  6, 6 },
239 	{ 3, "0 E", -(int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000,  6, 6 },
240 	{ 4, "92 P", (int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000,  5, 6 },
241 	{ 5, "-92 P", -(int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000,  5, 6 },
242 	{ 4, "91 P", (int64_t)81*1024*1024*1024*1024*1024L, HN_DIVISOR_1000,  5, 6 },
243 	{ 5, "-91 P", -(int64_t)81*1024*1024*1024*1024*1024L, HN_DIVISOR_1000,  5, 6 },
244 
245 	/* Autoscale, divisor 1024 */
246 	{ 3, "8 E",   INT64_MAX, 0, HN_AUTOSCALE, 6 },
247 	{ 4, "-8 E", -INT64_MAX, 0, HN_AUTOSCALE, 6 },
248 	{ 4, "92 P", (int64_t)92*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE, 6 },
249 	{ 5, "-92 P", -(int64_t)92*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE, 6 },
250 	{ 4, "82 P", (int64_t)82*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE, 6 },
251 	{ 5, "-82 P", -(int64_t)82*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE, 6 },
252 	{ 4, "81 P", (int64_t)81*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE, 6 },
253 	{ 5, "-81 P", -(int64_t)81*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE, 6 },
254 	/* Autoscale, divisor 1000 */
255 	{ 3, "9 E",   INT64_MAX, HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
256 	{ 4, "-9 E", -INT64_MAX, HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
257 	{ 4, "92 P", (int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
258 	{ 5, "-92 P", -(int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
259 	{ 4, "91 P", (int64_t)81*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
260 	{ 5, "-91 P", -(int64_t)81*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
261 
262 	/* 0 scale, divisor 1024 */
263 	{ 12, "skdj",  INT64_MAX, 0, 0, 6 },
264 	{ 21, "-9223", -INT64_MAX, 0, 0, 6 },
265 	{ 19, "10358", (int64_t)92*1024*1024*1024*1024*1024L, 0, 0, 6 },
266 	{ 20, "-1035", -(int64_t)92*1024*1024*1024*1024*1024L, 0, 0, 6 },
267 	{ 18, "92323", (int64_t)82*1024*1024*1024*1024*1024L, 0, 0, 6 },
268 	{ 19, "-9232", -(int64_t)82*1024*1024*1024*1024*1024L, 0, 0, 6 },
269 	{ 18, "91197", (int64_t)81*1024*1024*1024*1024*1024L, 0, 0, 6 },
270 	{ 19, "-9119", -(int64_t)81*1024*1024*1024*1024*1024L, 0, 0, 6 },
271 
272 	/* 0 scale, divisor 1000 */
273 	/* XXX - why does this fail? */
274 	{ -1, "", INT64_MAX, HN_DIVISOR_1000, 0, 6 },
275 	{ 21, "-9223", -INT64_MAX, HN_DIVISOR_1000,  0, 6 },
276 	{ 19, "10358", (int64_t)92*1024*1024*1024*1024*1024L, HN_DIVISOR_1000,  0, 6 },
277 	{ 20, "-1035", -(int64_t)92*1024*1024*1024*1024*1024L, HN_DIVISOR_1000,  0, 6 },
278 	{ 18, "92323", (int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000,  0, 6 },
279 	{ 19, "-9232", -(int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000,  0, 6 },
280 	/* Expected to pass */
281 	{ 18, "91197", (int64_t)81*1024*1024*1024*1024*1024L, HN_DIVISOR_1000,  0, 6 },
282 	{ 19, "-9119", -(int64_t)81*1024*1024*1024*1024*1024L, HN_DIVISOR_1000,  0, 6 },
283 
284 
285 
286 	/* Need to implement tests for GETSCALE */
287 /*	{ ?, "", (int64_t)0L, HN_DIVISOR_1000, HN_GETSCALE, 6 },
288 	...
289 */
290         /* Tests for HN_DECIMAL */
291         /* Positive, Autoscale */
292 	{ 5, "500 k", (int64_t)500*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
293 	{ 5, "994 k", (int64_t)994*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
294 	{ 5, "995 k", (int64_t)995*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
295 	{ 5, "999 k", (int64_t)999*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
296 	{ 5, "1.0 M", (int64_t)1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
297 	{ 5, "1.5 M", (int64_t)1500*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
298 	{ 5, "1.9 M", (int64_t)1949*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
299 	{ 5, "2.0 M", (int64_t)1950*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
300 	{ 5, "9.9 M", (int64_t)9949*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
301 	{ 4, "10 M", (int64_t)9950*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
302 	{ 5, "500 M", (int64_t)500*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
303 	{ 5, "994 M", (int64_t)994*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
304 	{ 5, "995 M", (int64_t)995*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
305 	{ 5, "999 M", (int64_t)999*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
306 
307 	{ 5, "500 K", (int64_t)500*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
308 	{ 5, "994 K", (int64_t)994*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
309 	{ 5, "995 K", (int64_t)995*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
310 	{ 5, "999 K", (int64_t)999*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
311 	{ 5, "1.0 M", (int64_t)1000*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
312 	{ 5, "1.0 M", (int64_t)1018*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
313 	{ 5, "1.0 M", (int64_t)1019*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
314 	{ 5, "1.5 M", (int64_t)1536*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
315 	{ 5, "1.9 M", (int64_t)1996*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
316 	{ 5, "2.0 M", (int64_t)1997*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
317 	{ 5, "2.0 M", (int64_t)2047*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
318 	{ 5, "2.0 M", (int64_t)2048*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
319 	{ 5, "2.0 M", (int64_t)2099*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
320 	{ 5, "2.1 M", (int64_t)2100*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
321 	{ 5, "9.9 M", (int64_t)10188*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
322 	/* XXX - shouldn't the following two be "10. M"? */
323 	{ 4, "10 M", (int64_t)10189*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
324 	{ 4, "10 M", (int64_t)10240*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
325 	{ 5, "500 M", (int64_t)500*1024*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
326 	{ 5, "994 M", (int64_t)994*1024*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
327 	{ 5, "995 M", (int64_t)995*1024*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
328 	{ 5, "999 M", (int64_t)999*1024*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
329 	{ 5, "1.0 G", (int64_t)1000*1024*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
330 	{ 5, "1.0 G", (int64_t)1023*1024*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
331 
332         /* Negative, Autoscale - should pass */
333 	{ 6, "-1.5 ", -(int64_t)1500*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
334 	{ 6, "-1.9 ", -(int64_t)1949*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
335 	{ 6, "-9.9 ", -(int64_t)9949*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
336 
337 	{ 6, "-1.5 ", -(int64_t)1536*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
338 	{ 6, "-1.9 ", -(int64_t)1949*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
339 	{ 6, "-9.7 ", -(int64_t)9949*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
340 
341 	/* Positive/negative, at maximum scale */
342 	{ 5, "500 P", (int64_t)500*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
343 	{ 5, "1.9 E", (int64_t)1949*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
344 	{ 5, "8.9 E", (int64_t)8949*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
345 	{ 5, "9.2 E", INT64_MAX, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
346         /* Negatives work with latest rev only: */
347 	{ 6, "-9.2 ", -INT64_MAX, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
348 	{ 6, "-8.9 ", -(int64_t)8949*1000*1000*1000*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
349 
350 	{ 5, "8.0 E",   INT64_MAX, HN_DECIMAL, HN_AUTOSCALE, 6 },
351 	{ 5, "7.9 E",   INT64_MAX-(int64_t)100*1024*1024*1024*1024*1024LL, HN_DECIMAL, HN_AUTOSCALE, 6 },
352 	{ 6, "-8.0 ", -INT64_MAX, HN_DECIMAL, HN_AUTOSCALE, 6 },
353 	{ 6, "-7.9 ",   -INT64_MAX+(int64_t)100*1024*1024*1024*1024*1024LL, HN_DECIMAL, HN_AUTOSCALE, 6 },
354 
355 	/* Positive, Fixed scales */
356 	{ 5, "500 k", (int64_t)500*1000L, HN_DECIMAL|HN_DIVISOR_1000, 1, 6 },
357 	{ 5, "0.5 M", (int64_t)500*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2, 6 },
358 	{ 5, "949 k", (int64_t)949*1000L, HN_DECIMAL|HN_DIVISOR_1000, 1, 6 },
359 	{ 5, "0.9 M", (int64_t)949*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2, 6 },
360 	{ 5, "950 k", (int64_t)950*1000L, HN_DECIMAL|HN_DIVISOR_1000, 1, 6 },
361 	{ 5, "1.0 M", (int64_t)950*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2, 6 },
362 	{ 5, "999 k", (int64_t)999*1000L, HN_DECIMAL|HN_DIVISOR_1000, 1, 6 },
363 	{ 5, "1.0 M", (int64_t)999*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2, 6 },
364 	{ 5, "1.5 M", (int64_t)1500*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2, 6 },
365 	{ 5, "1.9 M", (int64_t)1949*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2, 6 },
366 	{ 5, "2.0 M", (int64_t)1950*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2, 6 },
367 	{ 5, "9.9 M", (int64_t)9949*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2, 6 },
368 	{ 4, "10 M", (int64_t)9950*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2, 6 },
369 	{ 5, "500 M", (int64_t)500*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2, 6 },
370 	{ 5, "0.5 G", (int64_t)500*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, 3, 6 },
371 	{ 5, "999 M", (int64_t)999*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2, 6 },
372 	{ 5, "1.0 G", (int64_t)999*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, 3, 6 },
373 	/* Positive/negative, at maximum scale */
374 	{ 5, "500 P", (int64_t)500*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, 5, 6 },
375 	{ 5, "1.0 E", (int64_t)500*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, 6, 6 },
376 	{ 5, "1.9 E", (int64_t)1949*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, 6, 6 },
377 	{ 5, "8.9 E", (int64_t)8949*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, 6, 6 },
378 	{ 5, "9.2 E", INT64_MAX, HN_DECIMAL|HN_DIVISOR_1000, 6, 6 },
379 
380 	/* HN_DECIMAL + binary + fixed scale cases not completed */
381 	{ 5, "512 K", (int64_t)512*1024L, HN_DECIMAL, 1, 6 },
382 	{ 5, "0.5 M", (int64_t)512*1024L, HN_DECIMAL, 2, 6 },
383 
384 	/* Negative, Fixed scales */
385         /* Not yet added, but should work with latest rev */
386 
387 };
388 
389 
390 /* Command line options usage */
391 static void
392 usage(char * progname) {
393 	printf("%s: tests libutil humanize_number function\n", progname);
394 	printf("Usage: %s [-nE] [-l num] [-v]\n\n", progname);
395 	printf("Options:\n");
396 	printf("\t-l num\tSet max length for result; buflen = num + 1\n");
397 	printf("\t\t  (NOTE: does not change expected result strings.)\n");
398 	printf("\t-n\tInclude negative scale tests, which cause older libutil\n");
399 	printf("\t\t  version of function to coredump with assertion failure\n");
400 	printf("\t-E\tInclude numbers > 1/2 Exa[byte] which currently fail\n");
401 	printf("\t-v\tVerbose - always print summary results\n");
402 	printf("\t-h, -?\tShow options\n");
403 }
404 
405 /* Parse command line options */
406 static void
407 read_options(int argc, char * const argv[], size_t *bufLength,
408     int *includeNegativeScale, int *includeExabytes, int *verbose) {
409 	int ch;
410 	size_t temp;
411 
412 	while ((ch = getopt(argc, argv, "nEh?vl:")) != -1) {
413 		switch (ch) {
414 			default:
415 				usage(argv[0]);
416 				exit(1);
417 				break;	/* UNREACHABLE */
418 			case 'h' :
419 			case '?' :
420 				usage(argv[0]);
421 				exit(0);
422 				break;	/* UNREACHABLE */
423 			case 'l' :
424 				sscanf(optarg, "%zu", &temp);
425 				*bufLength = temp + 1;
426 				break;
427 			case 'n' :
428 				*includeNegativeScale = 1;
429 				break;
430 			case 'E' :
431 				*includeExabytes = 1;
432 				break;
433 			case 'v' :
434 				*verbose = 1;
435 				break;
436 		}
437 	}
438 }
439 
440 static struct {
441 	int value;
442 	const char *name;
443  } flags[] = {
444 	{ HN_AUTOSCALE, "HN_AUTOSCALE" },
445 	{ HN_GETSCALE, "HN_GETSCALE" },
446 	{ HN_DIVISOR_1000, "HN_DIVISOR_1000" },
447 	{ HN_B, "HN_B" },
448 	{ HN_DECIMAL, "HN_DECIMAL" },
449 };
450 
451 static const char *separator = "|";
452 
453 /* Format flags parameter for meaningful display */
454 static char *
455 str_flags(int hn_flags, char *noFlags) {
456 	size_t i;
457 	char * result;
458 
459 	result = malloc(MAX_STR_FLAGS_RESULT);
460 	result[0] = '\0';
461 
462 	for (i = 0; i < sizeof flags / sizeof *flags; i++) {
463 		if (hn_flags & flags[i].value) {
464 			if (*result != 0)
465 				strlcat(result, separator,
466 				    MAX_STR_FLAGS_RESULT);
467 			strlcat(result, flags[i].name, MAX_STR_FLAGS_RESULT);
468 		}
469 	}
470 
471 	if (strlen(result) == 0)
472 		strlcat(result, noFlags, MAX_STR_FLAGS_RESULT);
473 	return result;
474 }
475 
476 
477 /* Format scale parameter for meaningful display */
478 static char *
479 str_scale(int scale) {
480 	char *result;
481 
482 	if (scale == HN_AUTOSCALE || scale == HN_GETSCALE)
483 		return str_flags(scale, "");
484 
485 	result = malloc(MAX_INT_STR_DIGITS);
486 	result[0] = '\0';
487 	snprintf(result, MAX_INT_STR_DIGITS, "%d", scale);
488 	return result;
489 }
490 
491 static void
492 testskipped(size_t i)
493 {
494 
495 	printf("ok %zu # skip - not turned on\n", i);
496 }
497 
498 int
499 main(int argc, char * const argv[])
500 {
501 	char *buf;
502 	char *flag_str, *scale_str;
503 	size_t blen, buflen, errcnt, i, skipped, tested;
504 	int r;
505 	int includeNegScale;
506 	int includeExabyteTests;
507 	int verbose;
508 
509 	buf = NULL;
510 	buflen = 0;
511 	includeNegScale = 0;
512 	includeExabyteTests = 0;
513 	verbose = 0;
514 
515 	read_options(argc, argv, &buflen, &includeNegScale,
516 	    &includeExabyteTests, &verbose);
517 
518 	errcnt = 0;
519 	tested = 0;
520 	skipped = 0;
521 
522 	if (buflen != 4)
523 		printf("Warning: buffer size %zu != 4, expect some results to differ.\n", buflen);
524 
525 	printf("1..%zu\n", nitems(test_args));
526 	for (i = 0; i < nitems(test_args); i++) {
527 		blen = (buflen > 0) ? buflen : test_args[i].buflen;
528 		buf = realloc(buf, blen);
529 
530 		if (test_args[i].scale < 0 && ! includeNegScale) {
531 			skipped++;
532 			testskipped(i + 1);
533 			continue;
534 		}
535 		if (test_args[i].num >= halfExabyte && ! includeExabyteTests) {
536 			skipped++;
537 			testskipped(i + 1);
538 			continue;
539 		}
540 
541 		r = humanize_number(buf, blen, test_args[i].num, "",
542 		    test_args[i].scale, test_args[i].flags);
543 		flag_str = str_flags(test_args[i].flags, "[no flags]");
544 		scale_str = str_scale(test_args[i].scale);
545 
546 		if (r != test_args[i].retval) {
547 			if (verbose)
548 				printf("wrong return value on index %zu, "
549 				    "buflen: %zu, got: %d + \"%s\", "
550 				    "expected %d + \"%s\"; num = %jd, "
551 				    "scale = %s, flags= %s.\n",
552 				    i, blen, r, buf, test_args[i].retval,
553 				    test_args[i].res,
554 				    (intmax_t)test_args[i].num,
555 				    scale_str, flag_str);
556 			else
557 				printf("not ok %zu # return %d != %d\n",
558 				    i + 1, r, test_args[i].retval);
559 			errcnt++;
560 		} else if (strcmp(buf, test_args[i].res) != 0) {
561 			if (verbose)
562 				printf("result mismatch on index %zu, got: "
563 				    "\"%s\", expected \"%s\"; num = %jd, "
564 				    "buflen: %zu, scale = %s, flags= %s.\n",
565 				    i, buf, test_args[i].res,
566 				    (intmax_t)test_args[i].num,
567 				    blen, scale_str, flag_str);
568 			else
569 				printf("not ok %zu # buf \"%s\" != \"%s\"\n",
570 				    i + 1, buf, test_args[i].res);
571 			errcnt++;
572 		} else {
573 			if (verbose)
574 				printf("successful result on index %zu, "
575 				    "returned %d, got: \"%s\"; num = %jd, "
576 				    "buflen = %zd, scale = %s, flags= %s.\n",
577 				    i, r, buf, (intmax_t)test_args[i].num,
578 				    blen, scale_str, flag_str);
579 			else
580 				printf("ok %zu\n", i + 1);
581 		}
582 		tested++;
583 	}
584 	free(buf);
585 
586 	if (verbose)
587 		printf("total errors: %zu/%zu tests, %zu skipped\n", errcnt,
588 		    tested, skipped);
589 
590 	if (errcnt)
591 		return 1;
592 
593 	return 0;
594 }
595