xref: /dpdk/app/test/test_cmdline_num.c (revision 5ac070cfed17111ccaf5aee0cc08119ebb1c4e5e)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2014 Intel Corporation
3  */
4 
5 #include <stdio.h>
6 #include <string.h>
7 #include <inttypes.h>
8 
9 #include <rte_string_fns.h>
10 
11 #include <cmdline_parse.h>
12 #include <cmdline_parse_num.h>
13 
14 #include "test_cmdline.h"
15 
16 struct num_unsigned_str {
17 	const char * str;
18 	uint64_t result;
19 };
20 
21 struct num_signed_str {
22 	const char * str;
23 	int64_t result;
24 };
25 
26 const struct num_unsigned_str num_valid_positive_strs[] = {
27 		/* decimal positive */
28 		{"0", 0 },
29 		{"127", INT8_MAX },
30 		{"128", INT8_MAX + 1 },
31 		{"255", UINT8_MAX },
32 		{"256", UINT8_MAX + 1 },
33 		{"32767", INT16_MAX },
34 		{"32768", INT16_MAX + 1 },
35 		{"65535", UINT16_MAX },
36 		{"65536", UINT16_MAX + 1 },
37 		{"2147483647", INT32_MAX },
38 		{"2147483648", INT32_MAX + 1U },
39 		{"4294967295", UINT32_MAX },
40 		{"4294967296", UINT32_MAX + 1ULL },
41 		{"9223372036854775807", INT64_MAX },
42 		{"9223372036854775808", INT64_MAX + 1ULL},
43 		{"18446744073709551615", UINT64_MAX },
44 		/* hexadecimal (no leading zeroes) */
45 		{"0x0", 0 },
46 		{"0x7F", INT8_MAX },
47 		{"0x80", INT8_MAX + 1 },
48 		{"0xFF", UINT8_MAX },
49 		{"0x100", UINT8_MAX + 1 },
50 		{"0x7FFF", INT16_MAX },
51 		{"0x8000", INT16_MAX + 1 },
52 		{"0xFFFF", UINT16_MAX },
53 		{"0x10000", UINT16_MAX + 1 },
54 		{"0x7FFFFFFF", INT32_MAX },
55 		{"0x80000000", INT32_MAX + 1U },
56 		{"0xFFFFFFFF", UINT32_MAX },
57 		{"0x100000000", UINT32_MAX + 1ULL },
58 		{"0x7FFFFFFFFFFFFFFF", INT64_MAX },
59 		{"0x8000000000000000", INT64_MAX + 1ULL},
60 		{"0xFFFFFFFFFFFFFFFF", UINT64_MAX },
61 		/* hexadecimal (with leading zeroes) */
62 		{"0x00", 0 },
63 		{"0x7F", INT8_MAX },
64 		{"0x80", INT8_MAX + 1 },
65 		{"0xFF", UINT8_MAX },
66 		{"0x0100", UINT8_MAX + 1 },
67 		{"0x7FFF", INT16_MAX },
68 		{"0x8000", INT16_MAX + 1 },
69 		{"0xFFFF", UINT16_MAX },
70 		{"0x00010000", UINT16_MAX + 1 },
71 		{"0x7FFFFFFF", INT32_MAX },
72 		{"0x80000000", INT32_MAX + 1U },
73 		{"0xFFFFFFFF", UINT32_MAX },
74 		{"0x0000000100000000", UINT32_MAX + 1ULL },
75 		{"0x7FFFFFFFFFFFFFFF", INT64_MAX },
76 		{"0x8000000000000000", INT64_MAX + 1ULL},
77 		{"0xFFFFFFFFFFFFFFFF", UINT64_MAX },
78 		/* check all characters */
79 		{"0x1234567890ABCDEF", 0x1234567890ABCDEFULL },
80 		{"0x1234567890abcdef", 0x1234567890ABCDEFULL },
81 		/* binary (no leading zeroes) */
82 		{"0b0", 0 },
83 		{"0b1111111", INT8_MAX },
84 		{"0b10000000", INT8_MAX + 1 },
85 		{"0b11111111", UINT8_MAX },
86 		{"0b100000000", UINT8_MAX + 1 },
87 		{"0b111111111111111", INT16_MAX },
88 		{"0b1000000000000000", INT16_MAX + 1 },
89 		{"0b1111111111111111", UINT16_MAX },
90 		{"0b10000000000000000", UINT16_MAX + 1 },
91 		{"0b1111111111111111111111111111111", INT32_MAX },
92 		{"0b10000000000000000000000000000000", INT32_MAX + 1U },
93 		{"0b11111111111111111111111111111111", UINT32_MAX },
94 		{"0b100000000000000000000000000000000", UINT32_MAX + 1ULL },
95 		{"0b111111111111111111111111111111111111111111111111111111111111111",
96 				INT64_MAX },
97 		{"0b1000000000000000000000000000000000000000000000000000000000000000",
98 				INT64_MAX + 1ULL},
99 		{"0b1111111111111111111111111111111111111111111111111111111111111111",
100 				UINT64_MAX },
101 		/* binary (with leading zeroes) */
102 		{"0b01111111", INT8_MAX },
103 		{"0b0000000100000000", UINT8_MAX + 1 },
104 		{"0b0111111111111111", INT16_MAX },
105 		{"0b00000000000000010000000000000000", UINT16_MAX + 1 },
106 		{"0b01111111111111111111111111111111", INT32_MAX },
107 		{"0b0000000000000000000000000000000100000000000000000000000000000000",
108 				UINT32_MAX + 1ULL },
109 		{"0b0111111111111111111111111111111111111111111111111111111111111111",
110 				INT64_MAX },
111 		/* octal */
112 		{"00", 0 },
113 		{"0177", INT8_MAX },
114 		{"0200", INT8_MAX + 1 },
115 		{"0377", UINT8_MAX },
116 		{"0400", UINT8_MAX + 1 },
117 		{"077777", INT16_MAX },
118 		{"0100000", INT16_MAX + 1 },
119 		{"0177777", UINT16_MAX },
120 		{"0200000", UINT16_MAX + 1 },
121 		{"017777777777", INT32_MAX },
122 		{"020000000000", INT32_MAX + 1U },
123 		{"037777777777", UINT32_MAX },
124 		{"040000000000", UINT32_MAX + 1ULL },
125 		{"0777777777777777777777", INT64_MAX },
126 		{"01000000000000000000000", INT64_MAX + 1ULL},
127 		{"01777777777777777777777", UINT64_MAX },
128 		/* check all numbers */
129 		{"012345670", 012345670 },
130 		{"076543210", 076543210 },
131 };
132 
133 const struct num_signed_str num_valid_negative_strs[] = {
134 		/* deciman negative */
135 		{"-128", INT8_MIN },
136 		{"-129", INT8_MIN - 1 },
137 		{"-32768", INT16_MIN },
138 		{"-32769", INT16_MIN - 1 },
139 		{"-2147483648", INT32_MIN },
140 		{"-2147483649", INT32_MIN - 1LL },
141 		{"-9223372036854775808", INT64_MIN },
142 };
143 
144 const struct num_unsigned_str num_garbage_positive_strs[] = {
145 		/* valid strings with garbage on the end, should still be valid */
146 		/* decimal */
147 		{"9223372036854775807\0garbage", INT64_MAX },
148 		{"9223372036854775807\tgarbage", INT64_MAX },
149 		{"9223372036854775807\rgarbage", INT64_MAX },
150 		{"9223372036854775807\ngarbage", INT64_MAX },
151 		{"9223372036854775807#garbage", INT64_MAX },
152 		{"9223372036854775807 garbage", INT64_MAX },
153 		/* hex */
154 		{"0x7FFFFFFFFFFFFFFF\0garbage", INT64_MAX },
155 		{"0x7FFFFFFFFFFFFFFF\tgarbage", INT64_MAX },
156 		{"0x7FFFFFFFFFFFFFFF\rgarbage", INT64_MAX },
157 		{"0x7FFFFFFFFFFFFFFF\ngarbage", INT64_MAX },
158 		{"0x7FFFFFFFFFFFFFFF#garbage", INT64_MAX },
159 		{"0x7FFFFFFFFFFFFFFF garbage", INT64_MAX },
160 		/* binary */
161 		{"0b1111111111111111111111111111111\0garbage", INT32_MAX },
162 		{"0b1111111111111111111111111111111\rgarbage", INT32_MAX },
163 		{"0b1111111111111111111111111111111\tgarbage", INT32_MAX },
164 		{"0b1111111111111111111111111111111\ngarbage", INT32_MAX },
165 		{"0b1111111111111111111111111111111#garbage", INT32_MAX },
166 		{"0b1111111111111111111111111111111 garbage", INT32_MAX },
167 		/* octal */
168 		{"01777777777777777777777\0garbage", UINT64_MAX },
169 		{"01777777777777777777777\rgarbage", UINT64_MAX },
170 		{"01777777777777777777777\tgarbage", UINT64_MAX },
171 		{"01777777777777777777777\ngarbage", UINT64_MAX },
172 		{"01777777777777777777777#garbage", UINT64_MAX },
173 		{"01777777777777777777777 garbage", UINT64_MAX },
174 };
175 
176 const struct num_signed_str num_garbage_negative_strs[] = {
177 		/* valid strings with garbage on the end, should still be valid */
178 		{"-9223372036854775808\0garbage", INT64_MIN },
179 		{"-9223372036854775808\rgarbage", INT64_MIN },
180 		{"-9223372036854775808\tgarbage", INT64_MIN },
181 		{"-9223372036854775808\ngarbage", INT64_MIN },
182 		{"-9223372036854775808#garbage", INT64_MIN },
183 		{"-9223372036854775808 garbage", INT64_MIN },
184 };
185 
186 const char * num_invalid_strs[] = {
187 		"18446744073709551616", /* out of range unsigned */
188 		"-9223372036854775809", /* out of range negative signed */
189 		"0x10000000000000000", /* out of range hex */
190 		/* out of range binary */
191 		"0b10000000000000000000000000000000000000000000000000000000000000000",
192 		"020000000000000000000000", /* out of range octal */
193 		/* wrong chars */
194 		"0123456239",
195 		"0x1234580AGE",
196 		"0b0111010101g001",
197 		"0b01110101017001",
198 		/* false negative numbers */
199 		"-12345F623",
200 		"-0x1234580A",
201 		"-0b0111010101",
202 		/* too long (128+ chars) */
203 		("0b1111000011110000111100001111000011110000111100001111000011110000"
204 		  "1111000011110000111100001111000011110000111100001111000011110000"),
205 		"1E3",
206 		"0A",
207 		"-B",
208 		"+4",
209 		"1.23G",
210 		"",
211 		" ",
212 		"#",
213 		"\r",
214 		"\t",
215 		"\n",
216 		"\0",
217 };
218 
219 static int
can_parse_unsigned(uint64_t expected_result,enum cmdline_numtype type)220 can_parse_unsigned(uint64_t expected_result, enum cmdline_numtype type)
221 {
222 	switch (type) {
223 	case RTE_UINT8:
224 		if (expected_result > UINT8_MAX)
225 			return 0;
226 		break;
227 	case RTE_UINT16:
228 		if (expected_result > UINT16_MAX)
229 			return 0;
230 		break;
231 	case RTE_UINT32:
232 		if (expected_result > UINT32_MAX)
233 			return 0;
234 		break;
235 	case RTE_INT8:
236 		if (expected_result > INT8_MAX)
237 			return 0;
238 		break;
239 	case RTE_INT16:
240 		if (expected_result > INT16_MAX)
241 			return 0;
242 		break;
243 	case RTE_INT32:
244 		if (expected_result > INT32_MAX)
245 			return 0;
246 		break;
247 	case RTE_INT64:
248 		if (expected_result > INT64_MAX)
249 			return 0;
250 		break;
251 	default:
252 		return 1;
253 	}
254 	return 1;
255 }
256 
257 static int
can_parse_signed(int64_t expected_result,enum cmdline_numtype type)258 can_parse_signed(int64_t expected_result, enum cmdline_numtype type)
259 {
260 	switch (type) {
261 	case RTE_UINT8:
262 		if (expected_result > UINT8_MAX || expected_result < 0)
263 			return 0;
264 		break;
265 	case RTE_UINT16:
266 		if (expected_result > UINT16_MAX || expected_result < 0)
267 			return 0;
268 		break;
269 	case RTE_UINT32:
270 		if (expected_result > UINT32_MAX || expected_result < 0)
271 			return 0;
272 		break;
273 	case RTE_UINT64:
274 		if (expected_result < 0)
275 			return 0;
276 		break;
277 	case RTE_INT8:
278 		if (expected_result > INT8_MAX || expected_result < INT8_MIN)
279 			return 0;
280 		break;
281 	case RTE_INT16:
282 		if (expected_result > INT16_MAX || expected_result < INT16_MIN)
283 			return 0;
284 		break;
285 	case RTE_INT32:
286 		if (expected_result > INT32_MAX || expected_result < INT32_MIN)
287 			return 0;
288 		break;
289 	default:
290 		return 1;
291 	}
292 	return 1;
293 }
294 
295 /* test invalid parameters */
296 int
test_parse_num_invalid_param(void)297 test_parse_num_invalid_param(void)
298 {
299 	char buf[CMDLINE_TEST_BUFSIZE];
300 	uint32_t result;
301 	cmdline_parse_token_num_t token;
302 	int ret = 0;
303 
304 	/* set up a token */
305 	token.num_data.type = RTE_UINT32;
306 
307 	/* copy string to buffer */
308 	strlcpy(buf, num_valid_positive_strs[0].str, sizeof(buf));
309 
310 	/* try all null */
311 	ret = cmdline_parse_num(NULL, NULL, NULL, 0);
312 	if (ret != -1) {
313 		printf("Error: parser accepted null parameters!\n");
314 		return -1;
315 	}
316 
317 	/* try null token */
318 	ret = cmdline_parse_num(NULL, buf, (void*)&result, sizeof(result));
319 	if (ret != -1) {
320 		printf("Error: parser accepted null token!\n");
321 		return -1;
322 	}
323 
324 	/* try null buf */
325 	ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token, NULL,
326 		(void*)&result, sizeof(result));
327 	if (ret != -1) {
328 		printf("Error: parser accepted null string!\n");
329 		return -1;
330 	}
331 
332 	/* try null result */
333 	ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token, buf,
334 		NULL, 0);
335 	if (ret == -1) {
336 		printf("Error: parser rejected null result!\n");
337 		return -1;
338 	}
339 
340 	/* test help function */
341 	memset(&buf, 0, sizeof(buf));
342 
343 	/* try all null */
344 	ret = cmdline_get_help_num(NULL, NULL, 0);
345 	if (ret != -1) {
346 		printf("Error: help function accepted null parameters!\n");
347 		return -1;
348 	}
349 
350 	/* try null token */
351 	ret = cmdline_get_help_num(NULL, buf, sizeof(buf));
352 	if (ret != -1) {
353 		printf("Error: help function accepted null token!\n");
354 		return -1;
355 	}
356 
357 	/* coverage! */
358 	ret = cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, buf, sizeof(buf));
359 	if (ret < 0) {
360 		printf("Error: help function failed with valid parameters!\n");
361 		return -1;
362 	}
363 
364 	return 0;
365 }
366 /* test valid parameters but invalid data */
367 int
test_parse_num_invalid_data(void)368 test_parse_num_invalid_data(void)
369 {
370 	enum cmdline_numtype type;
371 	int ret = 0;
372 	unsigned i;
373 	char buf[CMDLINE_TEST_BUFSIZE];
374 	uint64_t result; /* pick largest buffer */
375 	cmdline_parse_token_num_t token;
376 
377 	/* cycle through all possible parsed types */
378 	for (type = RTE_UINT8; type <= RTE_INT64; type++) {
379 		token.num_data.type = type;
380 
381 		/* test full strings */
382 		for (i = 0; i < RTE_DIM(num_invalid_strs); i++) {
383 
384 			memset(&result, 0, sizeof(uint64_t));
385 			memset(&buf, 0, sizeof(buf));
386 
387 			ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token,
388 				num_invalid_strs[i], (void*)&result, sizeof(result));
389 			if (ret != -1) {
390 				/* get some info about what we are trying to parse */
391 				cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
392 						buf, sizeof(buf));
393 
394 				printf("Error: parsing %s as %s succeeded!\n",
395 						num_invalid_strs[i], buf);
396 				return -1;
397 			}
398 		}
399 	}
400 	return 0;
401 }
402 
403 /* test valid parameters and data */
404 int
test_parse_num_valid(void)405 test_parse_num_valid(void)
406 {
407 	int ret = 0;
408 	enum cmdline_numtype type;
409 	unsigned i;
410 	char buf[CMDLINE_TEST_BUFSIZE];
411 	uint64_t result;
412 	cmdline_parse_token_num_t token;
413 
414 	/** valid strings **/
415 
416 	/* cycle through all possible parsed types */
417 	for (type = RTE_UINT8; type <= RTE_INT64; type++) {
418 		token.num_data.type = type;
419 
420 		/* test positive strings */
421 		for (i = 0; i < RTE_DIM(num_valid_positive_strs); i++) {
422 			result = 0;
423 			memset(&buf, 0, sizeof(buf));
424 
425 			cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
426 					buf, sizeof(buf));
427 
428 			ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
429 				num_valid_positive_strs[i].str,
430 				(void*)&result, sizeof(result));
431 
432 			/* if it should have passed but didn't, or if it should have failed but didn't */
433 			if ((ret < 0) == (can_parse_unsigned(num_valid_positive_strs[i].result, type) > 0)) {
434 				printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
435 						num_valid_positive_strs[i].str, buf);
436 				return -1;
437 			}
438 			/* check if result matches what it should have matched
439 			 * since unsigned numbers don't care about number of bits, we can just convert
440 			 * everything to uint64_t without any worries. */
441 			if (ret > 0 && num_valid_positive_strs[i].result != result) {
442 				printf("Error: parsing %s as %s failed: result mismatch!\n",
443 						num_valid_positive_strs[i].str, buf);
444 				return -1;
445 			}
446 		}
447 
448 		/* test negative strings */
449 		for (i = 0; i < RTE_DIM(num_valid_negative_strs); i++) {
450 			result = 0;
451 			memset(&buf, 0, sizeof(buf));
452 
453 			cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
454 					buf, sizeof(buf));
455 
456 			ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
457 				num_valid_negative_strs[i].str,
458 				(void*)&result, sizeof(result));
459 
460 			/* if it should have passed but didn't, or if it should have failed but didn't */
461 			if ((ret < 0) == (can_parse_signed(num_valid_negative_strs[i].result, type) > 0)) {
462 				printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
463 						num_valid_negative_strs[i].str, buf);
464 				return -1;
465 			}
466 			/* check if result matches what it should have matched
467 			 * the result is signed in this case, so we have to account for that */
468 			if (ret > 0) {
469 				/* detect negative */
470 				switch (type) {
471 				case RTE_INT8:
472 					result = (int8_t) result;
473 					break;
474 				case RTE_INT16:
475 					result = (int16_t) result;
476 					break;
477 				case RTE_INT32:
478 					result = (int32_t) result;
479 					break;
480 				default:
481 					break;
482 				}
483 				if (num_valid_negative_strs[i].result == (int64_t) result)
484 					continue;
485 				printf("Error: parsing %s as %s failed: result mismatch!\n",
486 						num_valid_negative_strs[i].str, buf);
487 				return -1;
488 			}
489 		}
490 	}
491 
492 	/** garbage strings **/
493 
494 	/* cycle through all possible parsed types */
495 	for (type = RTE_UINT8; type <= RTE_INT64; type++) {
496 		token.num_data.type = type;
497 
498 		/* test positive garbage strings */
499 		for (i = 0; i < RTE_DIM(num_garbage_positive_strs); i++) {
500 			result = 0;
501 			memset(&buf, 0, sizeof(buf));
502 
503 			cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
504 					buf, sizeof(buf));
505 
506 			ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
507 				num_garbage_positive_strs[i].str,
508 				(void*)&result, sizeof(result));
509 
510 			/* if it should have passed but didn't, or if it should have failed but didn't */
511 			if ((ret < 0) == (can_parse_unsigned(num_garbage_positive_strs[i].result, type) > 0)) {
512 				printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
513 						num_garbage_positive_strs[i].str, buf);
514 				return -1;
515 			}
516 			/* check if result matches what it should have matched
517 			 * since unsigned numbers don't care about number of bits, we can just convert
518 			 * everything to uint64_t without any worries. */
519 			if (ret > 0 && num_garbage_positive_strs[i].result != result) {
520 				printf("Error: parsing %s as %s failed: result mismatch!\n",
521 						num_garbage_positive_strs[i].str, buf);
522 				return -1;
523 			}
524 		}
525 
526 		/* test negative strings */
527 		for (i = 0; i < RTE_DIM(num_garbage_negative_strs); i++) {
528 			result = 0;
529 			memset(&buf, 0, sizeof(buf));
530 
531 			cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
532 					buf, sizeof(buf));
533 
534 			ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
535 				num_garbage_negative_strs[i].str,
536 				(void*)&result, sizeof(result));
537 
538 			/* if it should have passed but didn't, or if it should have failed but didn't */
539 			if ((ret < 0) == (can_parse_signed(num_garbage_negative_strs[i].result, type) > 0)) {
540 				printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
541 						num_garbage_negative_strs[i].str, buf);
542 				return -1;
543 			}
544 			/* check if result matches what it should have matched
545 			 * the result is signed in this case, so we have to account for that */
546 			if (ret > 0) {
547 				/* detect negative */
548 				switch (type) {
549 				case RTE_INT8:
550 					if (result & (INT8_MAX + 1))
551 						result |= 0xFFFFFFFFFFFFFF00ULL;
552 					break;
553 				case RTE_INT16:
554 					if (result & (INT16_MAX + 1))
555 						result |= 0xFFFFFFFFFFFF0000ULL;
556 					break;
557 				case RTE_INT32:
558 					if (result & (INT32_MAX + 1ULL))
559 						result |= 0xFFFFFFFF00000000ULL;
560 					break;
561 				default:
562 					break;
563 				}
564 				if (num_garbage_negative_strs[i].result == (int64_t) result)
565 					continue;
566 				printf("Error: parsing %s as %s failed: result mismatch!\n",
567 						num_garbage_negative_strs[i].str, buf);
568 				return -1;
569 			}
570 		}
571 	}
572 
573 	memset(&buf, 0, sizeof(buf));
574 
575 	/* coverage! */
576 	cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
577 			buf, sizeof(buf));
578 
579 	return 0;
580 }
581