xref: /illumos-gate/usr/src/test/os-tests/tests/ilstr/ilstr_basic.c (revision 15cacbd5271ba21ede8d4c34e75a5547374b3e71)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2025 Oxide Computer Company
14  */
15 
16 /*
17  * Basic tests for the ilstr string handling routines.
18  */
19 
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <strings.h>
24 #include <errno.h>
25 #include <upanic.h>
26 #include <umem.h>
27 #include <sys/ilstr.h>
28 #include <sys/sysmacros.h>
29 #include <sys/debug.h>
30 
31 #define	VERIFYSTRING(ils, expected)				\
32 	do {							\
33 		const char *check = ilstr_cstr(ils);		\
34 		VERIFY(check != NULL);				\
35 		if (strcmp(check, (expected)) != 0) {		\
36 			char buf[2048];				\
37 			(void) snprintf(buf, sizeof (buf),	\
38 			    "\"%s\" != expected \"%s\"",	\
39 			    check, (expected));			\
40 			assfail(buf, __FILE__, __LINE__);	\
41 		}						\
42 	} while (0)
43 
44 typedef enum ilstr_test_types {
45 	ITT_STD = 0x01,
46 	ITT_PRE = 0x02,
47 } ilstr_test_types_t;
48 
49 #define	ITT_ALL	(ITT_STD | ITT_PRE)
50 
51 typedef struct ilstr_test {
52 	char *ist_name;
53 	int (*ist_func)(ilstr_t *ils);
54 	uint_t ist_trials;
55 	ilstr_test_types_t ist_types;
56 } ilstr_test_t;
57 
58 #define	PREALLOC_SZ	1024
59 static char ilsbuf[PREALLOC_SZ];
60 
61 const char *
_umem_debug_init(void)62 _umem_debug_init(void)
63 {
64 	return ("default,verbose");
65 }
66 
67 const char *
_umem_logging_init(void)68 _umem_logging_init(void)
69 {
70 	return ("transaction,contents,fail");
71 }
72 
73 int
ist_empty(ilstr_t * ils)74 ist_empty(ilstr_t *ils)
75 {
76 	VERIFY3U(ilstr_len(ils), ==, 0);
77 	VERIFY(ilstr_is_empty(ils));
78 	VERIFY(ilstr_cstr(ils) != NULL);
79 	VERIFY3U(ilstr_cstr(ils)[0], ==, '\0');
80 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
81 
82 	return (0);
83 }
84 
85 int
ist_prealloc_toobig(ilstr_t * ils)86 ist_prealloc_toobig(ilstr_t *ils)
87 {
88 	/*
89 	 * Fill the buffer all the way up with characters:
90 	 */
91 	for (uint_t n = 0; n < PREALLOC_SZ - 1; n++) {
92 		ilstr_append_str(ils, "A");
93 	}
94 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
95 
96 	/*
97 	 * Confirm that we cannot append strings:
98 	 */
99 	ilstr_append_str(ils, "A");
100 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
101 
102 	ilstr_append_str(ils, "A");
103 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
104 
105 	/*
106 	 * Confirm that we cannot prepend a character:
107 	 */
108 	ilstr_prepend_char(ils, 'A');
109 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
110 
111 	/*
112 	 * Clear out the string and make sure we can start again:
113 	 */
114 	ilstr_reset(ils);
115 
116 	ilstr_append_str(ils, "B");
117 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
118 	VERIFYSTRING(ils, "B");
119 
120 	/*
121 	 * Fill the buffer all the way up with characters:
122 	 */
123 	ilstr_reset(ils);
124 	for (uint_t n = 0; n < PREALLOC_SZ - 1; n++) {
125 		ilstr_append_str(ils, "C");
126 	}
127 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
128 
129 	/*
130 	 * Confirm that we cannot prepend strings:
131 	 */
132 	ilstr_prepend_str(ils, "D");
133 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
134 
135 	ilstr_prepend_str(ils, "D");
136 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
137 
138 	/*
139 	 * Confirm that we cannot append a character:
140 	 */
141 	ilstr_append_char(ils, 'D');
142 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
143 
144 	return (0);
145 }
146 
147 int
ist_standard_toobig(ilstr_t * ils)148 ist_standard_toobig(ilstr_t *ils)
149 {
150 	/*
151 	 * Pack the buffer with characters, so that we've been forced to
152 	 * allocate at least one extra chunk:
153 	 */
154 	for (uint_t n = 0; n < PREALLOC_SZ - 1; n++) {
155 		ilstr_append_str(ils, "A");
156 	}
157 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
158 
159 	/*
160 	 * Continue adding characters until we hit apparent memory exhaustion:
161 	 */
162 	for (uint_t n = 0; n < PREALLOC_SZ - 1; n++) {
163 		/*
164 		 * Use the umem fault injection facility to fail any
165 		 * allocations made while we append to the string:
166 		 */
167 		umem_setmtbf(1);
168 		ilstr_append_str(ils, "A");
169 		umem_setmtbf(0);
170 
171 		if (ilstr_errno(ils) == ILSTR_ERROR_NOMEM) {
172 			break;
173 		}
174 
175 		VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
176 	}
177 
178 	/*
179 	 * Confirm that we ran out of memory along the way, and didn't write
180 	 * too many characters in the process:
181 	 */
182 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
183 	VERIFY3U(ilstr_len(ils), <, 2 * PREALLOC_SZ);
184 
185 	return (0);
186 }
187 
188 int
ist_huge(ilstr_t * ils)189 ist_huge(ilstr_t *ils)
190 {
191 	/*
192 	 * Build a 26MB string by repeating the alphabet over and over:
193 	 */
194 	uint_t target = 26 * 1024 * 1024;
195 
196 	for (uint_t n = 0; n < target / 26; n++) {
197 		ilstr_append_str(ils, "abcdefghijklmnopqrstuvwxyz");
198 
199 		if (ilstr_errno(ils) == ILSTR_ERROR_NOMEM) {
200 			return (ENOMEM);
201 		}
202 		VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
203 	}
204 
205 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
206 	VERIFY3U(ilstr_len(ils), ==, target);
207 	VERIFY(!ilstr_is_empty(ils));
208 
209 	return (0);
210 }
211 
212 int
ist_printf_1(ilstr_t * ils)213 ist_printf_1(ilstr_t *ils)
214 {
215 	const char *want = "a\nb\n1000\ntest string\n";
216 
217 	ilstr_aprintf(ils, "a\nb\n%u\n%s\n", 1000, "test string");
218 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
219 	VERIFYSTRING(ils, want);
220 
221 	return (0);
222 }
223 
224 int
ist_printf_2(ilstr_t * ils)225 ist_printf_2(ilstr_t *ils)
226 {
227 	int r = 0;
228 
229 	const char *lorem = "Lorem ipsum dolor sit amet, consectetur "
230 	    "adipiscing elit, sed do eiusmod tempor incididunt ut labore "
231 	    "et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud "
232 	    "exercitation ullamco laboris nisi ut aliquip ex ea commodo "
233 	    "consequat.";
234 	char *want;
235 
236 	if (asprintf(&want, "%s\n\tnumber 1\n%s\n\n%s\n   number 100000000\n",
237 	    lorem, lorem, lorem) < 0) {
238 		return (errno);
239 	}
240 
241 	ilstr_aprintf(ils, "%s\n\t", lorem);
242 	ilstr_append_str(ils, "number");
243 	ilstr_aprintf(ils, " %u\n%s\n\n", 1, lorem);
244 	ilstr_append_str(ils, lorem);
245 	ilstr_aprintf(ils, "\n   number %lld\n", (long long)100000000);
246 
247 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
248 	if (strcmp(ilstr_cstr(ils), want) != 0) {
249 		printf("want: %s\n", want);
250 		printf("got:  %s\n", ilstr_cstr(ils));
251 		r = ENOENT;
252 	}
253 
254 	free(want);
255 
256 	return (r);
257 }
258 
259 int
ist_resets(ilstr_t * ils)260 ist_resets(ilstr_t *ils)
261 {
262 	VERIFYSTRING(ils, "");
263 
264 	ilstr_reset(ils);
265 	VERIFYSTRING(ils, "");
266 
267 	ilstr_append_str(ils, "abc");
268 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
269 	VERIFYSTRING(ils, "abc");
270 
271 	ilstr_append_str(ils, "def");
272 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
273 	VERIFYSTRING(ils, "abcdef");
274 
275 	ilstr_reset(ils);
276 	VERIFYSTRING(ils, "");
277 
278 	ilstr_append_str(ils, "xyz");
279 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
280 	VERIFYSTRING(ils, "xyz");
281 
282 	ilstr_reset(ils);
283 	VERIFY(strcmp(ilstr_cstr(ils), "") == 0);
284 	VERIFYSTRING(ils, "");
285 
286 	return (0);
287 }
288 
289 int
ist_random(ilstr_t * ils)290 ist_random(ilstr_t *ils)
291 {
292 	char *work;
293 	uint_t target = 256 + arc4random_uniform(1024 - 256);
294 
295 	printf(" - target string length %u\n", target);
296 	if ((work = calloc(1, 1024 + 1)) == NULL) {
297 		return (errno);
298 	}
299 
300 	VERIFY3U(ilstr_len(ils), ==, 0);
301 	VERIFY(ilstr_is_empty(ils));
302 	VERIFY3U(ilstr_cstr(ils)[0], ==, '\0');
303 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
304 
305 	for (uint_t n = 0; n < target; n++) {
306 		char c[2] = { arc4random_uniform('Z' - 'A') + 'A', '\0' };
307 
308 		work[n] = c[0];
309 		ilstr_append_str(ils, c);
310 
311 		VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
312 		VERIFY3U(ilstr_len(ils), ==, n + 1);
313 		VERIFY(!ilstr_is_empty(ils));
314 		VERIFYSTRING(ils, work);
315 	}
316 
317 	VERIFY(!ilstr_is_empty(ils));
318 	VERIFY3U(ilstr_len(ils), ==, target);
319 	VERIFYSTRING(ils, work);
320 	printf(" - final string: %s\n", work);
321 
322 	free(work);
323 	return (0);
324 }
325 
326 int
ist_prepend_char(ilstr_t * ils)327 ist_prepend_char(ilstr_t *ils)
328 {
329 	ilstr_append_str(ils, "ackwards");
330 	ilstr_prepend_char(ils, 'B');
331 	ilstr_append_char(ils, '!');
332 
333 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
334 	VERIFYSTRING(ils, "Backwards!");
335 
336 	return (0);
337 }
338 
339 int
ist_prepend_str(ilstr_t * ils)340 ist_prepend_str(ilstr_t *ils)
341 {
342 	ilstr_prepend_str(ils, "string was prepended");
343 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
344 	VERIFYSTRING(ils, "string was prepended");
345 
346 	ilstr_prepend_str(ils, "this ");
347 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
348 	VERIFYSTRING(ils, "this string was prepended");
349 
350 	ilstr_append_str(ils, ", successfully");
351 	VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
352 	VERIFYSTRING(ils, "this string was prepended, successfully");
353 
354 	return (0);
355 }
356 
357 int
ist_building_list(ilstr_t * ils)358 ist_building_list(ilstr_t *ils)
359 {
360 	const char *items[] = { "one", "two", "three" };
361 	const char *expect[] = {
362 		"empty list",
363 		"populated list (one)",
364 		"populated list (one, two)",
365 		"populated list (one, two, three)",
366 	};
367 
368 	for (uint_t n = 0; n <= 3; n++) {
369 		ilstr_reset(ils);
370 
371 		for (uint_t i = 0; i < n; i++) {
372 			if (!ilstr_is_empty(ils)) {
373 				ilstr_append_str(ils, ", ");
374 			}
375 
376 			ilstr_append_str(ils, items[i]);
377 		}
378 
379 		if (ilstr_is_empty(ils)) {
380 			ilstr_append_str(ils, "empty list");
381 		} else {
382 			ilstr_prepend_str(ils, "populated list (");
383 			ilstr_append_str(ils, ")");
384 		}
385 
386 		VERIFYSTRING(ils, expect[n]);
387 	}
388 
389 	return (0);
390 }
391 
392 uint_t
ist_drive_test(const ilstr_test_t * ist)393 ist_drive_test(const ilstr_test_t *ist)
394 {
395 	uint_t nfails = 0;
396 	int r;
397 	ilstr_t ils;
398 
399 	for (uint_t n = 0; n < ist->ist_trials; n++) {
400 		if (ist->ist_types & ITT_STD) {
401 			ilstr_init(&ils, 0);
402 			printf("STD[%s]... run %d\n", ist->ist_name, n);
403 			if ((r = ist->ist_func(&ils)) != 0) {
404 				(void) fprintf(stderr,
405 				    "TEST FAILED: STD[%s]: %s\n",
406 				    ist->ist_name, strerror(r));
407 				nfails += 1;
408 			} else {
409 				printf("TEST PASSED: STD[%s]\n",
410 				    ist->ist_name);
411 			}
412 			ilstr_fini(&ils);
413 			printf("\n");
414 		}
415 
416 		if (ist->ist_types & ITT_PRE) {
417 			ilstr_init_prealloc(&ils, ilsbuf, sizeof (ilsbuf));
418 			printf("PRE[%s]... run %d\n", ist->ist_name, n);
419 			if ((r = ist->ist_func(&ils)) != 0) {
420 				(void) fprintf(stderr,
421 				    "TEST FAILED: PRE[%s]: %s\n",
422 				    ist->ist_name, strerror(r));
423 				nfails += 1;
424 			} else {
425 				printf("TEST PASSED: PRE[%s]\n",
426 				    ist->ist_name);
427 			}
428 			ilstr_fini(&ils);
429 			printf("\n");
430 		}
431 	}
432 
433 	return (nfails);
434 }
435 
436 static const ilstr_test_t ilstr_tests[] = {
437 	{ "empty",		ist_empty,		1,	ITT_ALL },
438 	{ "resets",		ist_resets,		1,	ITT_ALL },
439 	{ "printf-1",		ist_printf_1,		1,	ITT_ALL },
440 	{ "printf-2",		ist_printf_2,		1,	ITT_ALL },
441 	{ "prealloc_toobig",	ist_prealloc_toobig,	1,	ITT_PRE },
442 	{ "standard_toobig",	ist_standard_toobig,	1,	ITT_STD },
443 	{ "prepend_char",	ist_prepend_char,	1,	ITT_ALL },
444 	{ "prepend_str",	ist_prepend_str,	1,	ITT_ALL },
445 	{ "building_list",	ist_building_list,	1,	ITT_ALL },
446 	/*
447 	 * Run the random generation test many times, as an attempt at fuzzing:
448 	 */
449 	{ "random",		ist_random,		1000,	ITT_ALL },
450 	/*
451 	 * Run the huge allocation test some number of times to try to make
452 	 * sure we exercise allocation and free of different buffer sizes, and
453 	 * to increase the likelihood of detecting any heap corruption:
454 	 */
455 	{ "huge",		ist_huge,		100,	ITT_STD },
456 };
457 
458 int
main(void)459 main(void)
460 {
461 	uint_t nfails = 0;
462 
463 	for (uint_t i = 0; i < ARRAY_SIZE(ilstr_tests); i++) {
464 		nfails += ist_drive_test(&ilstr_tests[i]);
465 	}
466 
467 	char *aoe;
468 	if ((aoe = getenv("PANIC_ON_EXIT")) != NULL && strcmp(aoe, "1") == 0) {
469 		const char *msg = "PANIC_ON_EXIT set; panicking for findleaks";
470 		upanic(msg, strlen(msg));
471 	}
472 
473 	return (nfails == 0 ? 0 : 1);
474 }
475