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 2023 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 <sys/ilstr.h>
27 #include <sys/sysmacros.h>
28 #include <sys/debug.h>
29
30 typedef enum ilstr_test_types {
31 ITT_STD = 0x01,
32 ITT_PRE = 0x02,
33 } ilstr_test_types_t;
34
35 #define ITT_ALL (ITT_STD | ITT_PRE)
36
37 typedef struct ilstr_test {
38 char *ist_name;
39 int (*ist_func)(ilstr_t *ils);
40 uint_t ist_trials;
41 ilstr_test_types_t ist_types;
42 } ilstr_test_t;
43
44 #define PREALLOC_SZ 1024
45 static char ilsbuf[PREALLOC_SZ];
46
47 const char *
_umem_debug_init(void)48 _umem_debug_init(void)
49 {
50 return ("default,verbose");
51 }
52
53 const char *
_umem_logging_init(void)54 _umem_logging_init(void)
55 {
56 return ("transaction,contents,fail");
57 }
58
59 int
ist_empty(ilstr_t * ils)60 ist_empty(ilstr_t *ils)
61 {
62 VERIFY3U(ilstr_len(ils), ==, 0);
63 VERIFY(ilstr_cstr(ils) != NULL);
64 VERIFY3U(ilstr_cstr(ils)[0], ==, '\0');
65 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
66
67 return (0);
68 }
69
70 int
ist_prealloc_toobig(ilstr_t * ils)71 ist_prealloc_toobig(ilstr_t *ils)
72 {
73 for (uint_t n = 0; n < PREALLOC_SZ - 1; n++) {
74 ilstr_append_str(ils, "A");
75 }
76 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
77
78 ilstr_append_str(ils, "A");
79 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
80
81 ilstr_append_str(ils, "A");
82 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
83
84 ilstr_reset(ils);
85
86 ilstr_append_str(ils, "B");
87 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
88 VERIFY(strcmp(ilstr_cstr(ils), "B") == 0);
89
90 return (0);
91 }
92
93 int
ist_huge(ilstr_t * ils)94 ist_huge(ilstr_t *ils)
95 {
96 /*
97 * Build a 26MB string by repeating the alphabet over and over:
98 */
99 uint_t target = 26 * 1024 * 1024;
100
101 for (uint_t n = 0; n < target / 26; n++) {
102 ilstr_append_str(ils, "abcdefghijklmnopqrstuvwxyz");
103
104 if (ilstr_errno(ils) == ILSTR_ERROR_NOMEM) {
105 return (ENOMEM);
106 }
107 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
108 }
109
110 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
111 VERIFY3U(ilstr_len(ils), ==, target);
112
113 return (0);
114 }
115
116 int
ist_printf_1(ilstr_t * ils)117 ist_printf_1(ilstr_t *ils)
118 {
119 const char *want = "a\nb\n1000\ntest string\n";
120
121 ilstr_aprintf(ils, "a\nb\n%u\n%s\n", 1000, "test string");
122 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
123 VERIFY(strcmp(ilstr_cstr(ils), want) == 0);
124
125 return (0);
126 }
127
128 int
ist_printf_2(ilstr_t * ils)129 ist_printf_2(ilstr_t *ils)
130 {
131 int r = 0;
132
133 const char *lorem = "Lorem ipsum dolor sit amet, consectetur "
134 "adipiscing elit, sed do eiusmod tempor incididunt ut labore "
135 "et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud "
136 "exercitation ullamco laboris nisi ut aliquip ex ea commodo "
137 "consequat.";
138 char *want;
139
140 if (asprintf(&want, "%s\n\tnumber 1\n%s\n\n%s\n number 100000000\n",
141 lorem, lorem, lorem) < 0) {
142 return (errno);
143 }
144
145 ilstr_aprintf(ils, "%s\n\t", lorem);
146 ilstr_append_str(ils, "number");
147 ilstr_aprintf(ils, " %u\n%s\n\n", 1, lorem);
148 ilstr_append_str(ils, lorem);
149 ilstr_aprintf(ils, "\n number %lld\n", (long long)100000000);
150
151 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
152 if (strcmp(ilstr_cstr(ils), want) != 0) {
153 printf("want: %s\n", want);
154 printf("got: %s\n", ilstr_cstr(ils));
155 r = ENOENT;
156 }
157
158 free(want);
159
160 return (r);
161 }
162
163 int
ist_resets(ilstr_t * ils)164 ist_resets(ilstr_t *ils)
165 {
166 VERIFY(strcmp(ilstr_cstr(ils), "") == 0);
167
168 ilstr_reset(ils);
169 VERIFY(strcmp(ilstr_cstr(ils), "") == 0);
170
171 ilstr_append_str(ils, "abc");
172 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
173 VERIFY(strcmp(ilstr_cstr(ils), "abc") == 0);
174
175 ilstr_append_str(ils, "def");
176 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
177 VERIFY(strcmp(ilstr_cstr(ils), "abcdef") == 0);
178
179 ilstr_reset(ils);
180 VERIFY(strcmp(ilstr_cstr(ils), "") == 0);
181
182 ilstr_append_str(ils, "xyz");
183 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
184 VERIFY(strcmp(ilstr_cstr(ils), "xyz") == 0);
185
186 ilstr_reset(ils);
187 VERIFY(strcmp(ilstr_cstr(ils), "") == 0);
188
189 return (0);
190 }
191
192 int
ist_random(ilstr_t * ils)193 ist_random(ilstr_t *ils)
194 {
195 char *work;
196 uint_t target = 256 + arc4random_uniform(1024 - 256);
197
198 printf(" - target string length %u\n", target);
199 if ((work = calloc(1, 1024 + 1)) == NULL) {
200 return (errno);
201 }
202
203 VERIFY3U(ilstr_len(ils), ==, 0);
204 VERIFY3U(ilstr_cstr(ils)[0], ==, '\0');
205 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
206
207 for (uint_t n = 0; n < target; n++) {
208 char c[2] = { arc4random_uniform('Z' - 'A') + 'A', '\0' };
209
210 work[n] = c[0];
211 ilstr_append_str(ils, c);
212
213 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
214 VERIFY3U(ilstr_len(ils), ==, n + 1);
215 VERIFY(strcmp(ilstr_cstr(ils), work) == 0);
216 }
217
218 VERIFY3U(ilstr_len(ils), ==, target);
219 VERIFY(strcmp(ilstr_cstr(ils), work) == 0);
220 printf(" - final string: %s\n", work);
221
222 free(work);
223 return (0);
224 }
225
226 uint_t
ist_drive_test(const ilstr_test_t * ist)227 ist_drive_test(const ilstr_test_t *ist)
228 {
229 uint_t nfails = 0;
230 int r;
231 ilstr_t ils;
232
233 for (uint_t n = 0; n < ist->ist_trials; n++) {
234 if (ist->ist_types & ITT_STD) {
235 ilstr_init(&ils, 0);
236 printf("STD[%s]... run %d\n", ist->ist_name, n);
237 if ((r = ist->ist_func(&ils)) != 0) {
238 (void) fprintf(stderr,
239 "TEST FAILED: STD[%s]: %s\n",
240 ist->ist_name, strerror(r));
241 nfails += 1;
242 } else {
243 printf("TEST PASSED: STD[%s]\n",
244 ist->ist_name);
245 }
246 ilstr_fini(&ils);
247 printf("\n");
248 }
249
250 if (ist->ist_types & ITT_PRE) {
251 ilstr_init_prealloc(&ils, ilsbuf, sizeof (ilsbuf));
252 printf("PRE[%s]... run %d\n", ist->ist_name, n);
253 if ((r = ist->ist_func(&ils)) != 0) {
254 (void) fprintf(stderr,
255 "TEST FAILED: PRE[%s]: %s\n",
256 ist->ist_name, strerror(r));
257 nfails += 1;
258 } else {
259 printf("TEST PASSED: PRE[%s]\n",
260 ist->ist_name);
261 }
262 ilstr_fini(&ils);
263 printf("\n");
264 }
265 }
266
267 return (nfails);
268 }
269
270 static const ilstr_test_t ilstr_tests[] = {
271 { "empty", ist_empty, 1, ITT_ALL },
272 { "resets", ist_resets, 1, ITT_ALL },
273 { "printf-1", ist_printf_1, 1, ITT_ALL },
274 { "printf-2", ist_printf_2, 1, ITT_ALL },
275 { "prealloc_toobig", ist_prealloc_toobig, 1, ITT_PRE },
276 /*
277 * Run the random generation test many times, as an attempt at fuzzing:
278 */
279 { "random", ist_random, 1000, ITT_ALL },
280 /*
281 * Run the huge allocation test some number of times to try to make
282 * sure we exercise allocation and free of different buffer sizes, and
283 * to increase the likelihood of detecting any heap corruption:
284 */
285 { "huge", ist_huge, 100, ITT_STD },
286 };
287
288 int
main(void)289 main(void)
290 {
291 uint_t nfails = 0;
292
293 for (uint_t i = 0; i < ARRAY_SIZE(ilstr_tests); i++) {
294 nfails += ist_drive_test(&ilstr_tests[i]);
295 }
296
297 char *aoe;
298 if ((aoe = getenv("PANIC_ON_EXIT")) != NULL && strcmp(aoe, "1") == 0) {
299 const char *msg = "PANIC_ON_EXIT set; panicking for findleaks";
300 upanic(msg, strlen(msg));
301 }
302
303 return (nfails == 0 ? 0 : 1);
304 }
305