]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - lib/libutil/tests/humanize_number_test.c
MFC r327317:
[FreeBSD/stable/10.git] / lib / libutil / tests / humanize_number_test.c
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 }