New upstream version 18.02
[deb_dpdk.git] / test / test / test_cmdline_num.c
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         snprintf(buf, sizeof(buf), "%s",
322                         num_valid_positive_strs[0].str);
323
324         /* try all null */
325         ret = cmdline_parse_num(NULL, NULL, NULL, 0);
326         if (ret != -1) {
327                 printf("Error: parser accepted null parameters!\n");
328                 return -1;
329         }
330
331         /* try null token */
332         ret = cmdline_parse_num(NULL, buf, (void*)&result, sizeof(result));
333         if (ret != -1) {
334                 printf("Error: parser accepted null token!\n");
335                 return -1;
336         }
337
338         /* try null buf */
339         ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token, NULL,
340                 (void*)&result, sizeof(result));
341         if (ret != -1) {
342                 printf("Error: parser accepted null string!\n");
343                 return -1;
344         }
345
346         /* try null result */
347         ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token, buf,
348                 NULL, 0);
349         if (ret == -1) {
350                 printf("Error: parser rejected null result!\n");
351                 return -1;
352         }
353
354         /* test help function */
355         memset(&buf, 0, sizeof(buf));
356
357         /* try all null */
358         ret = cmdline_get_help_num(NULL, NULL, 0);
359         if (ret != -1) {
360                 printf("Error: help function accepted null parameters!\n");
361                 return -1;
362         }
363
364         /* try null token */
365         ret = cmdline_get_help_num(NULL, buf, sizeof(buf));
366         if (ret != -1) {
367                 printf("Error: help function accepted null token!\n");
368                 return -1;
369         }
370
371         /* coverage! */
372         ret = cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, buf, sizeof(buf));
373         if (ret < 0) {
374                 printf("Error: help function failed with valid parameters!\n");
375                 return -1;
376         }
377
378         return 0;
379 }
380 /* test valid parameters but invalid data */
381 int
382 test_parse_num_invalid_data(void)
383 {
384         enum cmdline_numtype type;
385         int ret = 0;
386         unsigned i;
387         char buf[CMDLINE_TEST_BUFSIZE];
388         uint64_t result; /* pick largest buffer */
389         cmdline_parse_token_num_t token;
390
391         /* cycle through all possible parsed types */
392         for (type = UINT8; type <= INT64; type++) {
393                 token.num_data.type = type;
394
395                 /* test full strings */
396                 for (i = 0; i < NUM_INVALID_STRS_SIZE; i++) {
397
398                         memset(&result, 0, sizeof(uint64_t));
399                         memset(&buf, 0, sizeof(buf));
400
401                         ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token,
402                                 num_invalid_strs[i], (void*)&result, sizeof(result));
403                         if (ret != -1) {
404                                 /* get some info about what we are trying to parse */
405                                 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
406                                                 buf, sizeof(buf));
407
408                                 printf("Error: parsing %s as %s succeeded!\n",
409                                                 num_invalid_strs[i], buf);
410                                 return -1;
411                         }
412                 }
413         }
414         return 0;
415 }
416
417 /* test valid parameters and data */
418 int
419 test_parse_num_valid(void)
420 {
421         int ret = 0;
422         enum cmdline_numtype type;
423         unsigned i;
424         char buf[CMDLINE_TEST_BUFSIZE];
425         uint64_t result;
426         cmdline_parse_token_num_t token;
427
428         /** valid strings **/
429
430         /* cycle through all possible parsed types */
431         for (type = UINT8; type <= INT64; type++) {
432                 token.num_data.type = type;
433
434                 /* test positive strings */
435                 for (i = 0; i < NUM_POSITIVE_STRS_SIZE; i++) {
436                         result = 0;
437                         memset(&buf, 0, sizeof(buf));
438
439                         cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
440                                         buf, sizeof(buf));
441
442                         ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
443                                 num_valid_positive_strs[i].str,
444                                 (void*)&result, sizeof(result));
445
446                         /* if it should have passed but didn't, or if it should have failed but didn't */
447                         if ((ret < 0) == (can_parse_unsigned(num_valid_positive_strs[i].result, type) > 0)) {
448                                 printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
449                                                 num_valid_positive_strs[i].str, buf);
450                                 return -1;
451                         }
452                         /* check if result matches what it should have matched
453                          * since unsigned numbers don't care about number of bits, we can just convert
454                          * everything to uint64_t without any worries. */
455                         if (ret > 0 && num_valid_positive_strs[i].result != result) {
456                                 printf("Error: parsing %s as %s failed: result mismatch!\n",
457                                                 num_valid_positive_strs[i].str, buf);
458                                 return -1;
459                         }
460                 }
461
462                 /* test negative strings */
463                 for (i = 0; i < NUM_NEGATIVE_STRS_SIZE; i++) {
464                         result = 0;
465                         memset(&buf, 0, sizeof(buf));
466
467                         cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
468                                         buf, sizeof(buf));
469
470                         ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
471                                 num_valid_negative_strs[i].str,
472                                 (void*)&result, sizeof(result));
473
474                         /* if it should have passed but didn't, or if it should have failed but didn't */
475                         if ((ret < 0) == (can_parse_signed(num_valid_negative_strs[i].result, type) > 0)) {
476                                 printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
477                                                 num_valid_negative_strs[i].str, buf);
478                                 return -1;
479                         }
480                         /* check if result matches what it should have matched
481                          * the result is signed in this case, so we have to account for that */
482                         if (ret > 0) {
483                                 /* detect negative */
484                                 switch (type) {
485                                 case INT8:
486                                         result = (int8_t) result;
487                                         break;
488                                 case INT16:
489                                         result = (int16_t) result;
490                                         break;
491                                 case INT32:
492                                         result = (int32_t) result;
493                                         break;
494                                 default:
495                                         break;
496                                 }
497                                 if (num_valid_negative_strs[i].result == (int64_t) result)
498                                         continue;
499                                 printf("Error: parsing %s as %s failed: result mismatch!\n",
500                                                 num_valid_negative_strs[i].str, buf);
501                                 return -1;
502                         }
503                 }
504         }
505
506         /** garbage strings **/
507
508         /* cycle through all possible parsed types */
509         for (type = UINT8; type <= INT64; type++) {
510                 token.num_data.type = type;
511
512                 /* test positive garbage strings */
513                 for (i = 0; i < NUM_POSITIVE_GARBAGE_STRS_SIZE; i++) {
514                         result = 0;
515                         memset(&buf, 0, sizeof(buf));
516
517                         cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
518                                         buf, sizeof(buf));
519
520                         ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
521                                 num_garbage_positive_strs[i].str,
522                                 (void*)&result, sizeof(result));
523
524                         /* if it should have passed but didn't, or if it should have failed but didn't */
525                         if ((ret < 0) == (can_parse_unsigned(num_garbage_positive_strs[i].result, type) > 0)) {
526                                 printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
527                                                 num_garbage_positive_strs[i].str, buf);
528                                 return -1;
529                         }
530                         /* check if result matches what it should have matched
531                          * since unsigned numbers don't care about number of bits, we can just convert
532                          * everything to uint64_t without any worries. */
533                         if (ret > 0 && num_garbage_positive_strs[i].result != result) {
534                                 printf("Error: parsing %s as %s failed: result mismatch!\n",
535                                                 num_garbage_positive_strs[i].str, buf);
536                                 return -1;
537                         }
538                 }
539
540                 /* test negative strings */
541                 for (i = 0; i < NUM_NEGATIVE_GARBAGE_STRS_SIZE; i++) {
542                         result = 0;
543                         memset(&buf, 0, sizeof(buf));
544
545                         cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
546                                         buf, sizeof(buf));
547
548                         ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
549                                 num_garbage_negative_strs[i].str,
550                                 (void*)&result, sizeof(result));
551
552                         /* if it should have passed but didn't, or if it should have failed but didn't */
553                         if ((ret < 0) == (can_parse_signed(num_garbage_negative_strs[i].result, type) > 0)) {
554                                 printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
555                                                 num_garbage_negative_strs[i].str, buf);
556                                 return -1;
557                         }
558                         /* check if result matches what it should have matched
559                          * the result is signed in this case, so we have to account for that */
560                         if (ret > 0) {
561                                 /* detect negative */
562                                 switch (type) {
563                                 case INT8:
564                                         if (result & (INT8_MAX + 1))
565                                                 result |= 0xFFFFFFFFFFFFFF00ULL;
566                                         break;
567                                 case INT16:
568                                         if (result & (INT16_MAX + 1))
569                                                 result |= 0xFFFFFFFFFFFF0000ULL;
570                                         break;
571                                 case INT32:
572                                         if (result & (INT32_MAX + 1ULL))
573                                                 result |= 0xFFFFFFFF00000000ULL;
574                                         break;
575                                 default:
576                                         break;
577                                 }
578                                 if (num_garbage_negative_strs[i].result == (int64_t) result)
579                                         continue;
580                                 printf("Error: parsing %s as %s failed: result mismatch!\n",
581                                                 num_garbage_negative_strs[i].str, buf);
582                                 return -1;
583                         }
584                 }
585         }
586
587         memset(&buf, 0, sizeof(buf));
588
589         /* coverage! */
590         cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
591                         buf, sizeof(buf));
592
593         return 0;
594 }