Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2010-2014 Intel Corporation
3 : : */
4 : :
5 : : #include <stdio.h>
6 : : #include <stdarg.h>
7 : : #include <stddef.h>
8 : : #include <inttypes.h>
9 : : #include <errno.h>
10 : : #include <string.h>
11 : :
12 : : #include <rte_string_fns.h>
13 : :
14 : : #include "test.h"
15 : :
16 : : #define LOG(...) do {\
17 : : fprintf(stderr, "%s() ln %d: ", __func__, __LINE__); \
18 : : fprintf(stderr, __VA_ARGS__); \
19 : : } while(0)
20 : :
21 : : #define DATA_BYTE 'a'
22 : :
23 : : static int
24 : 1 : test_rte_strsplit(void)
25 : : {
26 : : int i;
27 : : do {
28 : : /* =======================================================
29 : : * split a mac address correct number of splits requested
30 : : * =======================================================*/
31 : 1 : char test_string[] = "54:65:76:87:98:90";
32 : : char *splits[6];
33 : :
34 : 1 : LOG("Source string: '%s', to split on ':'\n", test_string);
35 [ - + ]: 1 : if (rte_strsplit(test_string, sizeof(test_string),
36 : : splits, 6, ':') != 6) {
37 : 0 : LOG("Error splitting mac address\n");
38 : 0 : return -1;
39 : : }
40 [ + + ]: 7 : for (i = 0; i < 6; i++)
41 : 6 : LOG("Token %d = %s\n", i + 1, splits[i]);
42 : : } while (0);
43 : :
44 : :
45 : : do {
46 : : /* =======================================================
47 : : * split on spaces smaller number of splits requested
48 : : * =======================================================*/
49 : 1 : char test_string[] = "54 65 76 87 98 90";
50 : : char *splits[6];
51 : :
52 : 1 : LOG("Source string: '%s', to split on ' '\n", test_string);
53 [ - + ]: 1 : if (rte_strsplit(test_string, sizeof(test_string),
54 : : splits, 3, ' ') != 3) {
55 : 0 : LOG("Error splitting mac address for max 2 splits\n");
56 : 0 : return -1;
57 : : }
58 [ + + ]: 4 : for (i = 0; i < 3; i++)
59 : 3 : LOG("Token %d = %s\n", i + 1, splits[i]);
60 : : } while (0);
61 : :
62 : : do {
63 : : /* =======================================================
64 : : * split on commas - more splits than commas requested
65 : : * =======================================================*/
66 : 1 : char test_string[] = "a,b,c,d";
67 : : char *splits[6];
68 : :
69 : 1 : LOG("Source string: '%s', to split on ','\n", test_string);
70 [ - + ]: 1 : if (rte_strsplit(test_string, sizeof(test_string),
71 : : splits, 6, ',') != 4) {
72 : 0 : LOG("Error splitting %s on ','\n", test_string);
73 : 0 : return -1;
74 : : }
75 [ + + ]: 5 : for (i = 0; i < 4; i++)
76 : 4 : LOG("Token %d = %s\n", i + 1, splits[i]);
77 : : } while(0);
78 : :
79 : : do {
80 : : /* =======================================================
81 : : * Try splitting on non-existent character.
82 : : * =======================================================*/
83 : 1 : char test_string[] = "a,b,c,d";
84 : : char *splits[6];
85 : :
86 : 1 : LOG("Source string: '%s', to split on ' '\n", test_string);
87 [ - + ]: 1 : if (rte_strsplit(test_string, sizeof(test_string),
88 : : splits, 6, ' ') != 1) {
89 : 0 : LOG("Error splitting %s on ' '\n", test_string);
90 : 0 : return -1;
91 : : }
92 : 1 : LOG("String not split\n");
93 : : } while(0);
94 : :
95 : : do {
96 : : /* =======================================================
97 : : * Invalid / edge case parameter checks
98 : : * =======================================================*/
99 : 1 : char test_string[] = "a,b,c,d";
100 : : char *splits[6];
101 : :
102 [ + - ]: 1 : if (rte_strsplit(NULL, 0, splits, 6, ',') >= 0
103 [ - + ]: 1 : || errno != EINVAL){
104 : 0 : LOG("Error: rte_strsplit accepted NULL string parameter\n");
105 : 0 : return -1;
106 : : }
107 : :
108 [ + - ]: 1 : if (rte_strsplit(test_string, sizeof(test_string), NULL, 0, ',') >= 0
109 [ - + ]: 1 : || errno != EINVAL){
110 : 0 : LOG("Error: rte_strsplit accepted NULL array parameter\n");
111 : 0 : return -1;
112 : : }
113 : :
114 : 1 : errno = 0;
115 [ + - - + ]: 1 : if (rte_strsplit(test_string, 0, splits, 6, ',') != 0 || errno != 0) {
116 : 0 : LOG("Error: rte_strsplit did not accept 0 length string\n");
117 : 0 : return -1;
118 : : }
119 : :
120 [ + - ]: 1 : if (rte_strsplit(test_string, sizeof(test_string), splits, 0, ',') != 0
121 [ - + ]: 1 : || errno != 0) {
122 : 0 : LOG("Error: rte_strsplit did not accept 0 length array\n");
123 : 0 : return -1;
124 : : }
125 : :
126 : 1 : LOG("Parameter test cases passed\n");
127 : : } while(0);
128 : :
129 : 1 : LOG("%s - PASSED\n", __func__);
130 : 1 : return 0;
131 : : }
132 : :
133 : : static int
134 : : test_rte_strlcat(void)
135 : : {
136 : : /* only run actual unit tests if we have system-provided strlcat */
137 : : #if defined(__BSD_VISIBLE) || defined(RTE_USE_LIBBSD)
138 : : #define BUF_LEN 32
139 : : const char dst[BUF_LEN] = "Test string";
140 : : const char src[] = " appended";
141 : : char bsd_dst[BUF_LEN];
142 : : char rte_dst[BUF_LEN];
143 : : size_t i, bsd_ret, rte_ret;
144 : :
145 : : LOG("dst = '%s', strlen(dst) = %zu\n", dst, strlen(dst));
146 : : LOG("src = '%s', strlen(src) = %zu\n", src, strlen(src));
147 : : LOG("---\n");
148 : :
149 : : for (i = 0; i < BUF_LEN; i++) {
150 : : /* initialize destination buffers */
151 : : memcpy(bsd_dst, dst, BUF_LEN);
152 : : memcpy(rte_dst, dst, BUF_LEN);
153 : : /* compare implementations */
154 : : bsd_ret = strlcat(bsd_dst, src, i);
155 : : rte_ret = rte_strlcat(rte_dst, src, i);
156 : : if (bsd_ret != rte_ret) {
157 : : LOG("Incorrect retval for buf length = %zu\n", i);
158 : : LOG("BSD: '%zu', rte: '%zu'\n", bsd_ret, rte_ret);
159 : : return -1;
160 : : }
161 : : if (memcmp(bsd_dst, rte_dst, BUF_LEN) != 0) {
162 : : LOG("Resulting buffers don't match\n");
163 : : LOG("BSD: '%s', rte: '%s'\n", bsd_dst, rte_dst);
164 : : return -1;
165 : : }
166 : : LOG("buffer size = %zu: dst = '%s', ret = %zu\n",
167 : : i, rte_dst, rte_ret);
168 : : }
169 : : LOG("Checked %zu combinations\n", i);
170 : : #undef BUF_LEN
171 : : #endif /* defined(__BSD_VISIBLE) || defined(RTE_USE_LIBBSD) */
172 : :
173 : : return 0;
174 : : }
175 : :
176 : : static int
177 : 1 : test_rte_str_skip_leading_spaces(void)
178 : : {
179 : : static const char empty[] = "";
180 : : static const char nowhitespace[] = "Thereisreallynowhitespace";
181 : : static const char somewhitespaces[] = " \f\n\r\t\vThere are some whitespaces";
182 : : const char *p;
183 : :
184 : 1 : LOG("Checking '%s'\n", empty);
185 : 1 : p = rte_str_skip_leading_spaces(empty);
186 [ - + ]: 1 : if (p != empty) {
187 : 0 : LOG("Returned address '%s' does not match expected result\n", p);
188 : 0 : return -1;
189 : : }
190 : 1 : LOG("Got expected '%s'\n", p);
191 : 1 : LOG("Checking '%s'\n", nowhitespace);
192 : 1 : p = rte_str_skip_leading_spaces(nowhitespace);
193 [ - + ]: 1 : if (p != nowhitespace) {
194 : 0 : LOG("Returned address '%s' does not match expected result\n", p);
195 : 0 : return -1;
196 : : }
197 : 1 : LOG("Got expected '%s'\n", p);
198 : 1 : LOG("Checking '%s'\n", somewhitespaces);
199 : 1 : p = rte_str_skip_leading_spaces(somewhitespaces);
200 [ - + ]: 1 : if (p != strchr(somewhitespaces, 'T')) {
201 : 0 : LOG("Returned address '%s' does not match expected result\n", p);
202 : 0 : return -1;
203 : : }
204 : 1 : LOG("Got expected '%s'\n", p);
205 : :
206 : 1 : return 0;
207 : : }
208 : :
209 : : static int
210 : 1 : test_rte_basename(void)
211 : : {
212 : : /* Test case structure for positive cases */
213 : : struct {
214 : : const char *input_path; /* Input path string */
215 : : const char *expected; /* Expected result */
216 : 1 : } test_cases[] = {
217 : : /* Test cases from man 3 basename */
218 : : {"/usr/lib", "lib"},
219 : : {"/usr/", "usr"},
220 : : {"usr", "usr"},
221 : : {"/", "/"},
222 : : {".", "."},
223 : : {"..", ".."},
224 : :
225 : : /* Additional requested test cases */
226 : : {"/////", "/"},
227 : : {"/path/to/file.txt", "file.txt"},
228 : :
229 : : /* Additional edge cases with trailing slashes */
230 : : {"///usr///", "usr"},
231 : : {"/a/b/c/", "c"},
232 : :
233 : : /* Empty string case */
234 : : {"", "."},
235 : : {NULL, "."} /* NULL path should return "." */
236 : : };
237 : :
238 : : char buf[256];
239 : : size_t result;
240 : :
241 : : /* Run positive test cases from table */
242 [ + + ]: 13 : for (size_t i = 0; i < RTE_DIM(test_cases); i++) {
243 : 12 : result = rte_basename(test_cases[i].input_path, buf, sizeof(buf));
244 : :
245 [ - + ]: 12 : if (strcmp(buf, test_cases[i].expected) != 0) {
246 : 0 : LOG("FAIL [%zu]: '%s' - buf contains '%s', expected '%s'\n",
247 : : i, test_cases[i].input_path, buf, test_cases[i].expected);
248 : 0 : return -1;
249 : : }
250 : :
251 : : /* Check that the return value matches the expected string length */
252 [ - + ]: 12 : if (result != strlen(test_cases[i].expected)) {
253 : 0 : LOG("FAIL [%zu]: '%s' - returned length %zu, expected %zu\n",
254 : : i, test_cases[i].input_path, result, strlen(test_cases[i].expected));
255 : 0 : return -1;
256 : : }
257 : :
258 : 12 : LOG("PASS [%zu]: '%s' -> '%s' (len=%zu)\n",
259 : : i, test_cases[i].input_path, buf, result);
260 : : }
261 : :
262 : : /* re-run the table above verifying that for a NULL buffer, or zero length, we get
263 : : * correct length returned.
264 : : */
265 [ + + ]: 13 : for (size_t i = 0; i < RTE_DIM(test_cases); i++) {
266 : 12 : result = rte_basename(test_cases[i].input_path, NULL, 0);
267 [ - + ]: 12 : if (result != strlen(test_cases[i].expected)) {
268 : 0 : LOG("FAIL [%zu]: '%s' - returned length %zu, expected %zu\n",
269 : : i, test_cases[i].input_path, result, strlen(test_cases[i].expected));
270 : 0 : return -1;
271 : : }
272 : 12 : LOG("PASS [%zu]: '%s' -> length %zu (NULL buffer case)\n",
273 : : i, test_cases[i].input_path, result);
274 : : }
275 : :
276 : : /* Test case: buffer too small for result should truncate and return full length */
277 : : const size_t small_size = 5;
278 : 1 : result = rte_basename("/path/to/very_long_filename.txt", buf, small_size);
279 : : /* Should be truncated to fit in 5 bytes (4 chars + null terminator) */
280 [ - + ]: 1 : if (strlen(buf) >= small_size) {
281 : 0 : LOG("FAIL: small buffer test - result '%s' not properly truncated (len=%zu, buflen=%zu)\n",
282 : : buf, strlen(buf), small_size);
283 : 0 : return -1;
284 : : }
285 : : /* Return value should indicate truncation occurred (>= buflen) */
286 [ - + ]: 1 : if (result != strlen("very_long_filename.txt")) {
287 : 0 : LOG("FAIL: small buffer test - return value %zu doesn't indicate truncation (buflen=%zu)\n",
288 : : result, small_size);
289 : 0 : return -1;
290 : : }
291 : 1 : LOG("PASS: small buffer truncation -> '%s' (returned len=%zu, actual len=%zu)\n",
292 : : buf, result, strlen(buf));
293 : :
294 : : /* extreme length test case - check that even with paths longer than PATH_MAX we still
295 : : * return the last component correctly. Use "/zzz...zzz/abc.txt" and check we get "abc.txt"
296 : : */
297 : 1 : char basename_val[] = "abc.txt";
298 : : char long_path[PATH_MAX + 50];
299 [ + + ]: 4117 : for (int i = 0; i < PATH_MAX + 20; i++)
300 [ + + ]: 8231 : long_path[i] = (i == 0) ? '/' : 'z';
301 : : sprintf(long_path + PATH_MAX + 20, "/%s", basename_val);
302 : :
303 : 1 : result = rte_basename(long_path, buf, sizeof(buf));
304 [ - + ]: 1 : if (strcmp(buf, basename_val) != 0) {
305 : 0 : LOG("FAIL: long path test - expected '%s', got '%s'\n",
306 : : basename_val, buf);
307 : 0 : return -1;
308 : : }
309 [ - + ]: 1 : if (result != strlen(basename_val)) {
310 : 0 : LOG("FAIL: long path test - expected length %zu, got %zu\n",
311 : : strlen(basename_val), result);
312 : 0 : return -1;
313 : : }
314 : 1 : LOG("PASS: long path test -> '%s' (len=%zu)\n", buf, result);
315 : 1 : return 0;
316 : : }
317 : :
318 : : static int
319 : 1 : test_rte_str_to_size(void)
320 : : {
321 : : struct {
322 : : const char *str;
323 : : uint64_t value;
324 : 1 : } valid_values[] = {
325 : : {"5G", (uint64_t)5 * 1024 * 1024 * 1024},
326 : : {"0x20g", (uint64_t)0x20 * 1024 * 1024 * 1024},
327 : : {"10M", 10 * 1024 * 1024},
328 : : {"050m", 050 * 1024 * 1024},
329 : : {"8K", 8 * 1024},
330 : : {"15k", 15 * 1024},
331 : : {"0200", 0200},
332 : : {"0x103", 0x103},
333 : : {"432", 432},
334 : : {"-1", 0},
335 : : {" -2", 0},
336 : : {" -3MB", 0},
337 : : };
338 : : struct {
339 : : const char *str;
340 : 1 : } invalid_values[] = {
341 : : /* we can only check for invalid input at the start of the string */
342 : : {"garbage"},
343 : : {""},
344 : : {" "},
345 : : };
346 : : unsigned int i;
347 : : uint64_t value;
348 : :
349 : 1 : LOG("Checking valid rte_str_to_size inputs\n");
350 : :
351 [ + + ]: 13 : for (i = 0; i < RTE_DIM(valid_values); i++) {
352 : 12 : errno = 0;
353 : 12 : value = rte_str_to_size(valid_values[i].str);
354 [ - + ]: 12 : if (value != valid_values[i].value) {
355 : 0 : LOG("FAIL: valid input '%s'\n", valid_values[i].str);
356 : 0 : return -1;
357 : : }
358 : 12 : LOG("PASS: valid input '%s' -> %" PRIu64 "\n",
359 : : valid_values[i].str, value);
360 : : }
361 : :
362 : 1 : LOG("Checking invalid rte_str_to_size inputs\n");
363 : :
364 [ + + ]: 4 : for (i = 0; i < RTE_DIM(invalid_values); i++) {
365 : 3 : errno = 0;
366 : 3 : (void)rte_str_to_size(invalid_values[i].str);
367 [ - + ]: 3 : if (errno == 0) {
368 : 0 : LOG("FAIL: invalid input '%s' did not set errno\n",
369 : : invalid_values[i].str);
370 : 0 : return -1;
371 : : }
372 : 3 : LOG("PASS: invalid input '%s' set errno=%d\n",
373 : : invalid_values[i].str, errno);
374 : : }
375 : :
376 : 1 : LOG("%s - PASSED\n", __func__);
377 : :
378 : 1 : return 0;
379 : : }
380 : :
381 : : static int
382 : 1 : test_string_fns(void)
383 : : {
384 [ + - ]: 1 : if (test_rte_strsplit() < 0)
385 : : return -1;
386 : : if (test_rte_strlcat() < 0)
387 : : return -1;
388 [ + - ]: 1 : if (test_rte_str_skip_leading_spaces() < 0)
389 : : return -1;
390 [ + - ]: 1 : if (test_rte_basename() < 0)
391 : : return -1;
392 [ - + ]: 1 : if (test_rte_str_to_size() < 0)
393 : 0 : return -1;
394 : : return 0;
395 : : }
396 : :
397 : 301 : REGISTER_FAST_TEST(string_autotest, NOHUGE_OK, ASAN_OK, test_string_fns);
|