humanize_number_test.c revision 335891
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: stable/10/lib/libutil/tests/humanize_number_test.c 335891 2018-07-03 14:57:11Z robak $
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
44static const int64_t halfExabyte = (int64_t)500*1000*1000*1000*1000*1000L;
45
46static 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 */
391static void
392usage(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 */
406static void
407read_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
440static 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
451static const char *separator = "|";
452
453/* Format flags parameter for meaningful display */
454static char *
455str_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 */
478static char *
479str_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
491static void
492testskipped(size_t i)
493{
494
495	printf("ok %zu # skip - not turned on\n", i);
496}
497
498int
499main(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