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