xref: /freebsd/lib/libutil/tests/expand_number_test.c (revision e7be843b4a162e68651d3911f0357ed464915629)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2019 John Baldwin <jhb@FreeBSD.org>
5  * Copyright (c) 2025 Dag-Erling Smørgrav <des@FreeBSD.org>
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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <atf-c.h>
30 #include <errno.h>
31 #include <libutil.h>
32 #include <stdint.h>
33 
34 static void
35 require_success(const char *str, int64_t exp_val)
36 {
37 	int64_t val;
38 
39 	ATF_REQUIRE_MSG(expand_number(str, &val) == 0,
40 	    "Failed to parse '%s': %m", str);
41 	ATF_REQUIRE_MSG(val == exp_val,
42 	    "String '%s' parsed as %jd instead of expected %jd", str,
43 	    (intmax_t)val, (intmax_t)exp_val);
44 }
45 
46 static void
47 require_error(const char *str, int exp_errno)
48 {
49 	int64_t val;
50 
51 	ATF_REQUIRE_MSG(expand_number(str, &val) == -1,
52 	    "String '%s' parsed as %jd instead of error", str, (intmax_t)val);
53 	ATF_REQUIRE_MSG(errno == exp_errno,
54 	    "String '%s' failed with %d instead of expected %d", str, errno,
55 	    exp_errno);
56 }
57 
58 ATF_TC_WITHOUT_HEAD(expand_number__ok);
59 ATF_TC_BODY(expand_number__ok, tp)
60 {
61 	/* Bare numbers. */
62 	require_success("-0", 0);
63 	require_success(" 0", 0);
64 	require_success("+0", 0);
65 	require_success("-1", -1);
66 	require_success(" 1", 1);
67 	require_success("+1", 1);
68 	require_success("-10", -10);
69 	require_success(" 10", 10);
70 	require_success("+10", 10);
71 
72 	/* Uppercase suffixes. */
73 	require_success("1B", 1);
74 	require_success("1K", 1LL << 10);
75 	require_success("1M", 1LL << 20);
76 	require_success("1G", 1LL << 30);
77 	require_success("1T", 1LL << 40);
78 	require_success("1P", 1LL << 50);
79 	require_success("1E", 1LL << 60);
80 
81 	/* Lowercase suffixes. */
82 	require_success("2b", 2);
83 	require_success("2k", 2LL << 10);
84 	require_success("2m", 2LL << 20);
85 	require_success("2g", 2LL << 30);
86 	require_success("2t", 2LL << 40);
87 	require_success("2p", 2LL << 50);
88 	require_success("2e", 2LL << 60);
89 
90 	/* Suffixes with a trailing 'b'. */
91 	require_success("3KB", 3LL << 10);
92 	require_success("3MB", 3LL << 20);
93 	require_success("3GB", 3LL << 30);
94 	require_success("3TB", 3LL << 40);
95 	require_success("3PB", 3LL << 50);
96 	require_success("3EB", 3LL << 60);
97 
98 	/* Negative numbers. */
99 	require_success("-1", -1);
100 	require_success("-10", -10);
101 	require_success("-1B", -1);
102 	require_success("-1K", -(1LL << 10));
103 	require_success("-1M", -(1LL << 20));
104 	require_success("-1G", -(1LL << 30));
105 	require_success("-1T", -(1LL << 40));
106 	require_success("-1P", -(1LL << 50));
107 	require_success("-1E", -(1LL << 60));
108 	require_success("-2b", -2);
109 	require_success("-2k", -(2LL << 10));
110 	require_success("-2m", -(2LL << 20));
111 	require_success("-2g", -(2LL << 30));
112 	require_success("-2t", -(2LL << 40));
113 	require_success("-2p", -(2LL << 50));
114 	require_success("-2e", -(2LL << 60));
115 	require_success("-3KB", -(3LL << 10));
116 	require_success("-3MB", -(3LL << 20));
117 	require_success("-3GB", -(3LL << 30));
118 	require_success("-3TB", -(3LL << 40));
119 	require_success("-3PB", -(3LL << 50));
120 	require_success("-3EB", -(3LL << 60));
121 
122 	/* Maximum values. */
123 	require_success("7E", 7LL << 60);
124 	require_success("8191P", 8191LL << 50);
125 	require_success("8388607T", 8388607LL << 40);
126 	require_success("8589934591G", 8589934591LL << 30);
127 	require_success("8796093022207M", 8796093022207LL << 20);
128 	require_success("9007199254740991K", 9007199254740991LL << 10);
129 	require_success("9223372036854775807", INT64_MAX);
130 
131 	/* Minimum values. */
132 	require_success("-7E", -(7LL << 60));
133 	require_success("-8191P", -(8191LL << 50));
134 	require_success("-8388607T", -(8388607LL << 40));
135 	require_success("-8589934591G", -(8589934591LL << 30));
136 	require_success("-8796093022207M", -(8796093022207LL << 20));
137 	require_success("-9007199254740991K", -(9007199254740991LL << 10));
138 	require_success("-9223372036854775808", INT64_MIN);
139 }
140 
141 ATF_TC_WITHOUT_HEAD(expand_number__bad);
142 ATF_TC_BODY(expand_number__bad, tp)
143 {
144 	/* No digits. */
145 	require_error("", EINVAL);
146 	require_error("b", EINVAL);
147 	require_error("k", EINVAL);
148 	require_error("m", EINVAL);
149 	require_error("g", EINVAL);
150 	require_error("t", EINVAL);
151 	require_error("p", EINVAL);
152 	require_error("e", EINVAL);
153 	require_error("-", EINVAL);
154 	require_error("-b", EINVAL);
155 	require_error("-k", EINVAL);
156 	require_error("-m", EINVAL);
157 	require_error("-g", EINVAL);
158 	require_error("-t", EINVAL);
159 	require_error("-p", EINVAL);
160 	require_error("-e", EINVAL);
161 
162 	require_error("not_a_number", EINVAL);
163 
164 	/* Invalid suffixes. */
165 	require_error("1a", EINVAL);
166 	require_error("1c", EINVAL);
167 	require_error("1d", EINVAL);
168 	require_error("1f", EINVAL);
169 	require_error("1h", EINVAL);
170 	require_error("1i", EINVAL);
171 	require_error("1j", EINVAL);
172 	require_error("1l", EINVAL);
173 	require_error("1n", EINVAL);
174 	require_error("1o", EINVAL);
175 	require_error("1q", EINVAL);
176 	require_error("1r", EINVAL);
177 	require_error("1s", EINVAL);
178 	require_error("1u", EINVAL);
179 	require_error("1v", EINVAL);
180 	require_error("1w", EINVAL);
181 	require_error("1x", EINVAL);
182 	require_error("1y", EINVAL);
183 	require_error("1z", EINVAL);
184 
185 	/* Trailing garbage. */
186 	require_error("1K foo", EINVAL);
187 	require_error("1Mfoo", EINVAL);
188 
189 	/* Overflow. */
190 	require_error("8E", ERANGE);
191 	require_error("8192P", ERANGE);
192 	require_error("8388608T", ERANGE);
193 	require_error("8589934592G", ERANGE);
194 	require_error("8796093022208M", ERANGE);
195 	require_error("9007199254740992K", ERANGE);
196 	require_error("9223372036854775808", ERANGE);
197 
198 	/* Multiple signs */
199 	require_error("--1", EINVAL);
200 	require_error("-+1", EINVAL);
201 	require_error("+-1", EINVAL);
202 	require_error("++1", EINVAL);
203 
204 	/* Whitespace after the sign */
205 	require_error(" - 1", EINVAL);
206 	require_error(" + 1", EINVAL);
207 }
208 
209 ATF_TC_WITHOUT_HEAD(expand_unsigned);
210 ATF_TC_BODY(expand_unsigned, tp)
211 {
212 	static struct tc {
213 		const char *str;
214 		uint64_t num;
215 		int error;
216 	} tcs[] = {
217 		{ "0", 0, 0 },
218 		{ "+0", 0, 0 },
219 		{ "-0", 0, 0 },
220 		{ "1", 1, 0 },
221 		{ "+1", 1, 0 },
222 		{ "-1", 0, ERANGE },
223 		{ "18446744073709551615", UINT64_MAX, 0 },
224 		{ "+18446744073709551615", UINT64_MAX, 0 },
225 		{ "-18446744073709551615", 0, ERANGE },
226 		{ 0 },
227 	};
228 	struct tc *tc;
229 	uint64_t num;
230 	int error, ret;
231 
232 	for (tc = tcs; tc->str != NULL; tc++) {
233 		ret = expand_number(tc->str, &num);
234 		error = errno;
235 		if (tc->error == 0) {
236 			ATF_REQUIRE_EQ_MSG(0, ret,
237 			    "%s ret = %d", tc->str, ret);
238 			ATF_REQUIRE_EQ_MSG(tc->num, num,
239 			    "%s num = %ju", tc->str, (uintmax_t)num);
240 		} else {
241 			ATF_REQUIRE_EQ_MSG(-1, ret,
242 			    "%s ret = %d", tc->str, ret);
243 			ATF_REQUIRE_EQ_MSG(tc->error, error,
244 			    "%s errno = %d", tc->str, error);
245 		}
246 	}
247 }
248 
249 ATF_TC_WITHOUT_HEAD(expand_generic);
250 ATF_TC_BODY(expand_generic, tp)
251 {
252 	uint64_t uint64;
253 	int64_t int64;
254 #ifdef __LP64__
255 	size_t size;
256 #endif
257 	off_t off;
258 
259 	ATF_REQUIRE_EQ(0, expand_number("18446744073709551615", &uint64));
260 	ATF_REQUIRE_EQ(UINT64_MAX, uint64);
261 	ATF_REQUIRE_EQ(-1, expand_number("-1", &uint64));
262 	ATF_REQUIRE_EQ(ERANGE, errno);
263 
264 	ATF_REQUIRE_EQ(0, expand_number("9223372036854775807", &int64));
265 	ATF_REQUIRE_EQ(INT64_MAX, int64);
266 	ATF_REQUIRE_EQ(-1, expand_number("9223372036854775808", &int64));
267 	ATF_REQUIRE_EQ(ERANGE, errno);
268 	ATF_REQUIRE_EQ(0, expand_number("-9223372036854775808", &int64));
269 	ATF_REQUIRE_EQ(INT64_MIN, int64);
270 
271 #ifdef __LP64__
272 	ATF_REQUIRE_EQ(0, expand_number("18446744073709551615", &size));
273 	ATF_REQUIRE_EQ(UINT64_MAX, size);
274 	ATF_REQUIRE_EQ(-1, expand_number("-1", &size));
275 	ATF_REQUIRE_EQ(ERANGE, errno);
276 #endif
277 
278 	ATF_REQUIRE_EQ(0, expand_number("9223372036854775807", &off));
279 	ATF_REQUIRE_EQ(INT64_MAX, off);
280 	ATF_REQUIRE_EQ(-1, expand_number("9223372036854775808", &off));
281 	ATF_REQUIRE_EQ(ERANGE, errno);
282 	ATF_REQUIRE_EQ(0, expand_number("-9223372036854775808", &off));
283 	ATF_REQUIRE_EQ(INT64_MIN, off);
284 }
285 
286 ATF_TP_ADD_TCS(tp)
287 {
288 	ATF_TP_ADD_TC(tp, expand_number__ok);
289 	ATF_TP_ADD_TC(tp, expand_number__bad);
290 	ATF_TP_ADD_TC(tp, expand_unsigned);
291 	ATF_TP_ADD_TC(tp, expand_generic);
292 
293 	return (atf_no_error());
294 }
295