xref: /dpdk/app/test/test_cmdline_num.c (revision 5ecb687a5698d2d8ec1f3b3b5a7a16bceca3e29c)
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 #define NUM_POSITIVE_STRS_SIZE \
220 	(sizeof(num_valid_positive_strs) / sizeof(num_valid_positive_strs[0]))
221 #define NUM_NEGATIVE_STRS_SIZE \
222 	(sizeof(num_valid_negative_strs) / sizeof(num_valid_negative_strs[0]))
223 #define NUM_POSITIVE_GARBAGE_STRS_SIZE \
224 	(sizeof(num_garbage_positive_strs) / sizeof(num_garbage_positive_strs[0]))
225 #define NUM_NEGATIVE_GARBAGE_STRS_SIZE \
226 	(sizeof(num_garbage_negative_strs) / sizeof(num_garbage_negative_strs[0]))
227 #define NUM_INVALID_STRS_SIZE \
228 	(sizeof(num_invalid_strs) / sizeof(num_invalid_strs[0]))
229 
230 
231 
232 static int
233 can_parse_unsigned(uint64_t expected_result, enum cmdline_numtype type)
234 {
235 	switch (type) {
236 	case UINT8:
237 		if (expected_result > UINT8_MAX)
238 			return 0;
239 		break;
240 	case UINT16:
241 		if (expected_result > UINT16_MAX)
242 			return 0;
243 		break;
244 	case UINT32:
245 		if (expected_result > UINT32_MAX)
246 			return 0;
247 		break;
248 	case INT8:
249 		if (expected_result > INT8_MAX)
250 			return 0;
251 		break;
252 	case INT16:
253 		if (expected_result > INT16_MAX)
254 			return 0;
255 		break;
256 	case INT32:
257 		if (expected_result > INT32_MAX)
258 			return 0;
259 		break;
260 	case INT64:
261 		if (expected_result > INT64_MAX)
262 			return 0;
263 		break;
264 	default:
265 		return 1;
266 	}
267 	return 1;
268 }
269 
270 static int
271 can_parse_signed(int64_t expected_result, enum cmdline_numtype type)
272 {
273 	switch (type) {
274 	case UINT8:
275 		if (expected_result > UINT8_MAX || expected_result < 0)
276 			return 0;
277 		break;
278 	case UINT16:
279 		if (expected_result > UINT16_MAX || expected_result < 0)
280 			return 0;
281 		break;
282 	case UINT32:
283 		if (expected_result > UINT32_MAX || expected_result < 0)
284 			return 0;
285 		break;
286 	case UINT64:
287 		if (expected_result < 0)
288 			return 0;
289 		break;
290 	case INT8:
291 		if (expected_result > INT8_MAX || expected_result < INT8_MIN)
292 			return 0;
293 		break;
294 	case INT16:
295 		if (expected_result > INT16_MAX || expected_result < INT16_MIN)
296 			return 0;
297 		break;
298 	case INT32:
299 		if (expected_result > INT32_MAX || expected_result < INT32_MIN)
300 			return 0;
301 		break;
302 	default:
303 		return 1;
304 	}
305 	return 1;
306 }
307 
308 /* test invalid parameters */
309 int
310 test_parse_num_invalid_param(void)
311 {
312 	char buf[CMDLINE_TEST_BUFSIZE];
313 	uint32_t result;
314 	cmdline_parse_token_num_t token;
315 	int ret = 0;
316 
317 	/* set up a token */
318 	token.num_data.type = UINT32;
319 
320 	/* copy string to buffer */
321 	strlcpy(buf, num_valid_positive_strs[0].str, sizeof(buf));
322 
323 	/* try all null */
324 	ret = cmdline_parse_num(NULL, NULL, NULL, 0);
325 	if (ret != -1) {
326 		printf("Error: parser accepted null parameters!\n");
327 		return -1;
328 	}
329 
330 	/* try null token */
331 	ret = cmdline_parse_num(NULL, buf, (void*)&result, sizeof(result));
332 	if (ret != -1) {
333 		printf("Error: parser accepted null token!\n");
334 		return -1;
335 	}
336 
337 	/* try null buf */
338 	ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token, NULL,
339 		(void*)&result, sizeof(result));
340 	if (ret != -1) {
341 		printf("Error: parser accepted null string!\n");
342 		return -1;
343 	}
344 
345 	/* try null result */
346 	ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token, buf,
347 		NULL, 0);
348 	if (ret == -1) {
349 		printf("Error: parser rejected null result!\n");
350 		return -1;
351 	}
352 
353 	/* test help function */
354 	memset(&buf, 0, sizeof(buf));
355 
356 	/* try all null */
357 	ret = cmdline_get_help_num(NULL, NULL, 0);
358 	if (ret != -1) {
359 		printf("Error: help function accepted null parameters!\n");
360 		return -1;
361 	}
362 
363 	/* try null token */
364 	ret = cmdline_get_help_num(NULL, buf, sizeof(buf));
365 	if (ret != -1) {
366 		printf("Error: help function accepted null token!\n");
367 		return -1;
368 	}
369 
370 	/* coverage! */
371 	ret = cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, buf, sizeof(buf));
372 	if (ret < 0) {
373 		printf("Error: help function failed with valid parameters!\n");
374 		return -1;
375 	}
376 
377 	return 0;
378 }
379 /* test valid parameters but invalid data */
380 int
381 test_parse_num_invalid_data(void)
382 {
383 	enum cmdline_numtype type;
384 	int ret = 0;
385 	unsigned i;
386 	char buf[CMDLINE_TEST_BUFSIZE];
387 	uint64_t result; /* pick largest buffer */
388 	cmdline_parse_token_num_t token;
389 
390 	/* cycle through all possible parsed types */
391 	for (type = UINT8; type <= INT64; type++) {
392 		token.num_data.type = type;
393 
394 		/* test full strings */
395 		for (i = 0; i < NUM_INVALID_STRS_SIZE; i++) {
396 
397 			memset(&result, 0, sizeof(uint64_t));
398 			memset(&buf, 0, sizeof(buf));
399 
400 			ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token,
401 				num_invalid_strs[i], (void*)&result, sizeof(result));
402 			if (ret != -1) {
403 				/* get some info about what we are trying to parse */
404 				cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
405 						buf, sizeof(buf));
406 
407 				printf("Error: parsing %s as %s succeeded!\n",
408 						num_invalid_strs[i], buf);
409 				return -1;
410 			}
411 		}
412 	}
413 	return 0;
414 }
415 
416 /* test valid parameters and data */
417 int
418 test_parse_num_valid(void)
419 {
420 	int ret = 0;
421 	enum cmdline_numtype type;
422 	unsigned i;
423 	char buf[CMDLINE_TEST_BUFSIZE];
424 	uint64_t result;
425 	cmdline_parse_token_num_t token;
426 
427 	/** valid strings **/
428 
429 	/* cycle through all possible parsed types */
430 	for (type = UINT8; type <= INT64; type++) {
431 		token.num_data.type = type;
432 
433 		/* test positive strings */
434 		for (i = 0; i < NUM_POSITIVE_STRS_SIZE; i++) {
435 			result = 0;
436 			memset(&buf, 0, sizeof(buf));
437 
438 			cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
439 					buf, sizeof(buf));
440 
441 			ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
442 				num_valid_positive_strs[i].str,
443 				(void*)&result, sizeof(result));
444 
445 			/* if it should have passed but didn't, or if it should have failed but didn't */
446 			if ((ret < 0) == (can_parse_unsigned(num_valid_positive_strs[i].result, type) > 0)) {
447 				printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
448 						num_valid_positive_strs[i].str, buf);
449 				return -1;
450 			}
451 			/* check if result matches what it should have matched
452 			 * since unsigned numbers don't care about number of bits, we can just convert
453 			 * everything to uint64_t without any worries. */
454 			if (ret > 0 && num_valid_positive_strs[i].result != result) {
455 				printf("Error: parsing %s as %s failed: result mismatch!\n",
456 						num_valid_positive_strs[i].str, buf);
457 				return -1;
458 			}
459 		}
460 
461 		/* test negative strings */
462 		for (i = 0; i < NUM_NEGATIVE_STRS_SIZE; i++) {
463 			result = 0;
464 			memset(&buf, 0, sizeof(buf));
465 
466 			cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
467 					buf, sizeof(buf));
468 
469 			ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
470 				num_valid_negative_strs[i].str,
471 				(void*)&result, sizeof(result));
472 
473 			/* if it should have passed but didn't, or if it should have failed but didn't */
474 			if ((ret < 0) == (can_parse_signed(num_valid_negative_strs[i].result, type) > 0)) {
475 				printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
476 						num_valid_negative_strs[i].str, buf);
477 				return -1;
478 			}
479 			/* check if result matches what it should have matched
480 			 * the result is signed in this case, so we have to account for that */
481 			if (ret > 0) {
482 				/* detect negative */
483 				switch (type) {
484 				case INT8:
485 					result = (int8_t) result;
486 					break;
487 				case INT16:
488 					result = (int16_t) result;
489 					break;
490 				case INT32:
491 					result = (int32_t) result;
492 					break;
493 				default:
494 					break;
495 				}
496 				if (num_valid_negative_strs[i].result == (int64_t) result)
497 					continue;
498 				printf("Error: parsing %s as %s failed: result mismatch!\n",
499 						num_valid_negative_strs[i].str, buf);
500 				return -1;
501 			}
502 		}
503 	}
504 
505 	/** garbage strings **/
506 
507 	/* cycle through all possible parsed types */
508 	for (type = UINT8; type <= INT64; type++) {
509 		token.num_data.type = type;
510 
511 		/* test positive garbage strings */
512 		for (i = 0; i < NUM_POSITIVE_GARBAGE_STRS_SIZE; i++) {
513 			result = 0;
514 			memset(&buf, 0, sizeof(buf));
515 
516 			cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
517 					buf, sizeof(buf));
518 
519 			ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
520 				num_garbage_positive_strs[i].str,
521 				(void*)&result, sizeof(result));
522 
523 			/* if it should have passed but didn't, or if it should have failed but didn't */
524 			if ((ret < 0) == (can_parse_unsigned(num_garbage_positive_strs[i].result, type) > 0)) {
525 				printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
526 						num_garbage_positive_strs[i].str, buf);
527 				return -1;
528 			}
529 			/* check if result matches what it should have matched
530 			 * since unsigned numbers don't care about number of bits, we can just convert
531 			 * everything to uint64_t without any worries. */
532 			if (ret > 0 && num_garbage_positive_strs[i].result != result) {
533 				printf("Error: parsing %s as %s failed: result mismatch!\n",
534 						num_garbage_positive_strs[i].str, buf);
535 				return -1;
536 			}
537 		}
538 
539 		/* test negative strings */
540 		for (i = 0; i < NUM_NEGATIVE_GARBAGE_STRS_SIZE; i++) {
541 			result = 0;
542 			memset(&buf, 0, sizeof(buf));
543 
544 			cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
545 					buf, sizeof(buf));
546 
547 			ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
548 				num_garbage_negative_strs[i].str,
549 				(void*)&result, sizeof(result));
550 
551 			/* if it should have passed but didn't, or if it should have failed but didn't */
552 			if ((ret < 0) == (can_parse_signed(num_garbage_negative_strs[i].result, type) > 0)) {
553 				printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
554 						num_garbage_negative_strs[i].str, buf);
555 				return -1;
556 			}
557 			/* check if result matches what it should have matched
558 			 * the result is signed in this case, so we have to account for that */
559 			if (ret > 0) {
560 				/* detect negative */
561 				switch (type) {
562 				case INT8:
563 					if (result & (INT8_MAX + 1))
564 						result |= 0xFFFFFFFFFFFFFF00ULL;
565 					break;
566 				case INT16:
567 					if (result & (INT16_MAX + 1))
568 						result |= 0xFFFFFFFFFFFF0000ULL;
569 					break;
570 				case INT32:
571 					if (result & (INT32_MAX + 1ULL))
572 						result |= 0xFFFFFFFF00000000ULL;
573 					break;
574 				default:
575 					break;
576 				}
577 				if (num_garbage_negative_strs[i].result == (int64_t) result)
578 					continue;
579 				printf("Error: parsing %s as %s failed: result mismatch!\n",
580 						num_garbage_negative_strs[i].str, buf);
581 				return -1;
582 			}
583 		}
584 	}
585 
586 	memset(&buf, 0, sizeof(buf));
587 
588 	/* coverage! */
589 	cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
590 			buf, sizeof(buf));
591 
592 	return 0;
593 }
594