xref: /xnu-10063.121.3/tests/test_strings.c (revision 2c2f96dc2b9a4408a43d3150ae9c105355ca3daa)
1 #define _FORTIFY_SOURCE 0
2 #define __arch_memcmp_zero_ptr_aligned
3 
4 /* must include first because otherwise header guard conflicts with SDK's
5  * string.h (quite reasonably)
6  */
7 #include "../osfmk/libsa/string.h"
8 
9 char *strerror(int);
10 char *itoa(int, char *);
11 
12 #include <darwintest.h>
13 #include <darwintest_utils.h>
14 
15 #pragma clang diagnostic ignored "-Wlanguage-extension-token"
16 #pragma clang diagnostic ignored "-Wformat-pedantic"
17 
18 #define DEVELOPMENT 0
19 #define DEBUG 0
20 #define XNU_KERNEL_PRIVATE 1
21 
22 __printflike(1, 2) __attribute__((noreturn))
23 static void
panic(const char * fmt,...)24 panic(const char *fmt, ...)
25 {
26 	va_list ap;
27 	va_start(ap, fmt);
28 	vfprintf(stderr, fmt, ap);
29 	abort();
30 }
31 
32 #include "../libkern/libkern/section_keywords.h"
33 #include "../osfmk/machine/string.h"
34 #include "../osfmk/device/subrs.c"
35 
36 #pragma clang diagnostic ignored "-Wdeclaration-after-statement"
37 #pragma clang diagnostic ignored "-Wgnu-designator"
38 
39 T_DECL(strbufcmp, "strbufcmp") {
40 #define T_COMPARE(A, AS, B, BS, EQ) T_ASSERT_EQ(strbufcmp_impl((A), (AS), (B), (BS)), (EQ), "compare '%s'.%zu, '%s'.%zu", (A), (AS), (B), (BS))
41 	// two identical strings
42 	char a[] = "hello";
43 	char b[] = "hello";
44 	T_COMPARE(a, sizeof(a), b, sizeof(b), 0);
45 	T_COMPARE(b, sizeof(b), a, sizeof(a), 0);
46 
47 	// the same string
48 	T_COMPARE(a, sizeof(a), b, sizeof(b), 0);
49 	T_COMPARE(b, sizeof(b), a, sizeof(a), 0);
50 
51 	// two different strings
52 	char c[] = "world";
53 	T_COMPARE(a, sizeof(a), c, sizeof(c), a[0] - c[0]);
54 	T_COMPARE(c, sizeof(c), a, sizeof(a), c[0] - a[0]);
55 	char d[] = "hellp";
56 	T_COMPARE(a, sizeof(a), d, sizeof(d), 'o' - 'p');
57 	T_COMPARE(d, sizeof(d), a, sizeof(a), 'p' - 'o');
58 
59 	// strings of different size
60 	char e[] = "aaaa";
61 	char f[] = "aaaab";
62 	T_COMPARE(e, sizeof(e), f, sizeof(f), 0 - 'b');
63 	T_COMPARE(f, sizeof(f), e, sizeof(e), 'b' - 0);
64 
65 	// strings that are not NUL-terminated
66 	T_COMPARE(a, sizeof(a) - 1, b, sizeof(b) - 1, 0);
67 	T_COMPARE(b, sizeof(b) - 1, a, sizeof(a) - 1, 0);
68 	T_COMPARE(a, sizeof(a) - 1, d, sizeof(d) - 1, 'o' - 'p');
69 	T_COMPARE(d, sizeof(d) - 1, a, sizeof(a) - 1, 'p' - 'o');
70 	T_COMPARE(e, sizeof(e) - 1, f, sizeof(f) - 1, 0 - 'b');
71 	T_COMPARE(f, sizeof(f) - 1, e, sizeof(e) - 1, 'b' - 0);
72 #undef T_COMPARE
73 }
74 
75 T_DECL(strlcmp, "strlcmp") {
76 #define T_COMPARE(A, AS, B, EQ) T_ASSERT_EQ(strlcmp_impl((A), (B), (AS)), (EQ), "compare '%s'.%zu, '%s'", (A), (AS), (B))
77 	// two identical strings
78 	char a[] = "hello";
79 	char b[] = "hello";
80 	T_COMPARE(a, sizeof(a), b, 0);
81 	T_COMPARE(b, sizeof(b), a, 0);
82 
83 	// the same string
84 	T_COMPARE(a, sizeof(a), b, 0);
85 	T_COMPARE(b, sizeof(b), a, 0);
86 
87 	// two different strings
88 	char c[] = "world";
89 	T_COMPARE(a, sizeof(a), c, a[0] - c[0]);
90 	T_COMPARE(c, sizeof(c), a, c[0] - a[0]);
91 	char d[] = "hellp";
92 	T_COMPARE(a, sizeof(a), d, 'o' - 'p');
93 	T_COMPARE(d, sizeof(d), a, 'p' - 'o');
94 
95 	// strings of different size
96 	char e[] = "aaaa";
97 	char f[] = "aaaab";
98 	T_COMPARE(e, sizeof(e), f, 0 - 'b');
99 	T_COMPARE(f, sizeof(f), e, 'b' - 0);
100 
101 	// strings that are not NUL-terminated
102 	T_COMPARE(a, sizeof(a) - 1, b, 0);
103 	T_COMPARE(b, sizeof(b) - 1, a, 0);
104 	T_COMPARE(a, sizeof(a) - 1, d, 'o' - 'p');
105 	T_COMPARE(d, sizeof(d) - 1, a, 'p' - 'o');
106 	T_COMPARE(e, sizeof(e) - 1, f, 0 - 'b');
107 	T_COMPARE(f, sizeof(f) - 1, e, 'b' - 0);
108 #undef T_COMPARE
109 }
110 
111 T_DECL(strbufcasecmp, "strbufcasecmp") {
112 #define T_COMPARE(A, AS, B, BS, EQ) T_ASSERT_EQ(strbufcasecmp_impl((A), (AS), (B), (BS)), (EQ), "case-insensitive compare '%s'.%zu, '%s'.%zu", (A), (AS), (B), (BS))
113 	// same tests as strcasecmp, then tests with individual characters
114 	// two identical strings
115 	char a[] = "hElLo";
116 	char b[] = "HeLlO";
117 	T_COMPARE(a, sizeof(a), b, sizeof(b), 0);
118 	T_COMPARE(b, sizeof(b), a, sizeof(a), 0);
119 
120 	// the same string
121 	T_COMPARE(a, sizeof(a), b, sizeof(b), 0);
122 	T_COMPARE(b, sizeof(b), a, sizeof(a), 0);
123 
124 	// two different strings
125 	char c[] = "world";
126 	T_COMPARE(a, sizeof(a), c, sizeof(c), a[0] - c[0]);
127 	T_COMPARE(c, sizeof(c), a, sizeof(a), c[0] - a[0]);
128 	char d[] = "hellp";
129 	T_COMPARE(a, sizeof(a), d, sizeof(d), 'o' - 'p');
130 	T_COMPARE(d, sizeof(d), a, sizeof(a), 'p' - 'o');
131 
132 	// strings of different size
133 	char e[] = "aAaA";
134 	char f[] = "AaAaB";
135 	T_COMPARE(e, sizeof(e), f, sizeof(f), 0 - 'b');
136 	T_COMPARE(f, sizeof(f), e, sizeof(e), 'b' - 0);
137 
138 	// strings that are not NUL-terminated
139 	T_COMPARE(a, sizeof(a) - 1, b, sizeof(b) - 1, 0);
140 	T_COMPARE(b, sizeof(b) - 1, a, sizeof(a) - 1, 0);
141 	T_COMPARE(a, sizeof(a) - 1, d, sizeof(d) - 1, 'o' - 'p');
142 	T_COMPARE(d, sizeof(d) - 1, a, sizeof(a) - 1, 'p' - 'o');
143 	T_COMPARE(e, sizeof(e) - 1, f, sizeof(f) - 1, 0 - 'b');
144 	T_COMPARE(f, sizeof(f) - 1, e, sizeof(e) - 1, 'b' - 0);
145 #undef T_COMPARE
146 }
147 
148 T_DECL(strlcasecmp, "strlcasecmp") {
149 #define T_COMPARE(A, AS, B, EQ) T_ASSERT_EQ(strlcasecmp_impl((A), (B), (AS)), (EQ), "case-insensitive compare '%s'.%zu, '%s'", (A), (AS), (B))
150 	// same tests as strcasecmp, then tests with individual characters
151 	// two identical strings
152 	char a[] = "hElLo";
153 	char b[] = "HeLlO";
154 	T_COMPARE(a, sizeof(a), b, 0);
155 	T_COMPARE(b, sizeof(b), a, 0);
156 
157 	// the same string
158 	T_COMPARE(a, sizeof(a), b, 0);
159 	T_COMPARE(b, sizeof(b), a, 0);
160 
161 	// two different strings
162 	char c[] = "world";
163 	T_COMPARE(a, sizeof(a), c, a[0] - c[0]);
164 	T_COMPARE(c, sizeof(c), a, c[0] - a[0]);
165 	char d[] = "hellp";
166 	T_COMPARE(a, sizeof(a), d, 'o' - 'p');
167 	T_COMPARE(d, sizeof(d), a, 'p' - 'o');
168 
169 	// strings of different size
170 	char e[] = "aAaA";
171 	char f[] = "AaAaB";
172 	T_COMPARE(e, sizeof(e), f, 0 - 'b');
173 	T_COMPARE(f, sizeof(f), e, 'b' - 0);
174 
175 	// strings that are not NUL-terminated
176 	T_COMPARE(a, sizeof(a) - 1, b, 0);
177 	T_COMPARE(b, sizeof(b) - 1, a, 0);
178 	T_COMPARE(a, sizeof(a) - 1, d, 'o' - 'p');
179 	T_COMPARE(d, sizeof(d) - 1, a, 'p' - 'o');
180 	T_COMPARE(e, sizeof(e) - 1, f, 0 - 'b');
181 	T_COMPARE(f, sizeof(f) - 1, e, 'b' - 0);
182 #undef T_COMPARE
183 }
184 
185 T_DECL(strbufcasecmp_all, "strbufcasecmp_all") {
186 #define T_CHAR_COMPARE(A, AS, B, BS, EQ) do { \
187     int r = strbufcasecmp_impl((A), (AS), (B), (BS)); \
188     if (r != (EQ)) T_FAIL("case-insensitive compare '0x%02hhx' to '0x%02hhx' was %i instead of %i", *(A), *(B), r, (EQ)); \
189 } while (0)
190 	// test each character
191 	char ga, gb, ha, hb;
192 	char nul = 0;
193 	for (int i = 0; i < 256; ++i) {
194 		ga = (char)(i);
195 		gb = (i >= 'A' && i <= 'Z') ? (char)(i - 'A' + 'a') : ga;
196 		T_CHAR_COMPARE(&ga, 1, &nul, 0, gb);
197 		T_CHAR_COMPARE(&nul, 0, &ga, 1, -gb);
198 
199 		for (int j = 0; j < 256; ++j) {
200 			ha = (char)(j);
201 			hb = (j >= 'A' && j <= 'Z') ? (char)(j - 'A' + 'a') : ha;
202 			T_CHAR_COMPARE(&ga, 1, &ha, 1, gb - hb);
203 			T_CHAR_COMPARE(&ha, 1, &ga, 1, hb - gb);
204 		}
205 	}
206 	T_PASS("ASCII character case insensitivity");
207 }
208 
209 T_DECL(strbufcpy, "strbufcpy") {
210 	char dst[32];
211 	// empty dest
212 	T_ASSERT_EQ(strbufcpy_impl(NULL, 0, "hello", 5), NULL, "0-length destination");
213 
214 #define T_CPY(A, AS, B, BS) T_ASSERT_EQ(strbufcpy_impl((A), (AS), (B), (BS)), (char *)(A), "copy '%.*s'.%zu to dst.%zu", (int)(BS), (B), (size_t)(BS), (AS))
215 	// copy NUL terminated string that fits in dst
216 	char hello[] = "hello";
217 	memset(dst, 0, sizeof(dst));
218 	T_CPY(dst, sizeof(dst), hello, sizeof(hello));
219 	T_ASSERT_EQ(memcmp_impl(dst, (char[32]){"hello"}, sizeof(dst)), 0, "check result is 'hello'");
220 
221 	// copy NUL terminated string that does not fit in dst
222 	char aaa[40] = {[0 ... 38] = 'a' };
223 	memset(dst, 0, sizeof(dst));
224 	T_CPY(dst, sizeof(dst), aaa, sizeof(aaa));
225 	T_ASSERT_EQ(memcmp_impl(aaa, dst, 31), 0, "check result is 'aaaaaa...'");
226 	T_ASSERT_EQ(dst[31], 0, "check result is NUL-terminated");
227 
228 	// copy non-terminated string
229 	memset(dst, 0xff, sizeof(dst));
230 	T_CPY(dst, sizeof(dst), "bbb", 3);
231 	T_ASSERT_EQ(strcmp_impl(dst, "bbb"), 0, "check result is 'bbb'");
232 
233 	// copy string over itself
234 	char hw1[32] = "hello world";
235 	T_CPY(hw1 + 6, sizeof(hw1) - 6, hw1, sizeof(hw1));
236 	T_ASSERT_EQ(strcmp_impl(hw1, "hello hello world"), 0, "check copy over self is 'hello hello world'");
237 
238 	char hw2[32] = "hello world";
239 	T_CPY(hw2, sizeof(hw2), hw2 + 6, sizeof(hw2) - 6);
240 	T_ASSERT_EQ(strcmp_impl(hw2, "world"), 0, "check copy over self is 'world'");
241 #undef T_CPY
242 }
243 
244 T_DECL(strbufcat, "strbufcat") {
245 	char dst[32] = {0};
246 
247 	// empty dst
248 	T_ASSERT_EQ(strbufcat_impl(NULL, 0, "hello", 5), NULL, "check concatenation to 0-length destination");
249 
250 #define T_CAT_RESULT(RESULT) \
251     T_ASSERT_EQ(strcmp_impl(dst, (RESULT)), 0, "check result of concatenation is '%s'", (RESULT)); \
252 
253 #define T_CAT(TO_CAT, RESULT) do { \
254     T_ASSERT_EQ(strbufcat_impl(dst, sizeof(dst), (TO_CAT), sizeof(TO_CAT)), (char *)dst, "check concatenation of '%s'", (TO_CAT)); \
255     T_CAT_RESULT(RESULT); \
256 } while (0)
257 
258 	// append "hello "
259 	T_CAT("hello ", "hello ");
260 
261 	// append "world!"
262 	T_CAT("world!", "hello world!");
263 
264 	// append itself
265 	T_ASSERT_EQ(strbufcat_impl(dst, sizeof(dst), dst, sizeof(dst)), (char *)dst, "check concatenating self");
266 	T_CAT_RESULT("hello world!hello world!");
267 
268 	// append bunch of 'a's
269 	T_ASSERT_EQ(strbufcat_impl(dst, sizeof(dst), "aaaaaaaaaa", 10), (char *)dst, "check concatenating 'aaaa...'");
270 	T_CAT_RESULT("hello world!hello world!aaaaaaa");
271 
272 #undef T_CAT
273 #undef T_CAT_RESULT
274 }
275 
276 T_DECL(libsa_overloads, "libsa_overloads") {
277 	char buf[32] = "hello, world";
278 	char buf2[32] = "world, hello";
279 
280 	T_ASSERT_EQ(strbuflen(buf), (size_t)12, "strbuflen one argument");
281 	T_ASSERT_EQ(strbuflen(buf, sizeof(buf)), (size_t)12, "strbuflen two arguments");
282 
283 	T_ASSERT_LT(strbufcmp(buf, buf2), 0, "strbufcmp two arguments");
284 	T_ASSERT_LT(strbufcmp(buf, sizeof(buf), buf2, sizeof(buf2)), 0, "strbufcmp four arguments");
285 
286 	T_ASSERT_LT(strbufcasecmp(buf, buf2), 0, "strbufcasecmp two arguments");
287 	T_ASSERT_LT(strbufcasecmp(buf, sizeof(buf), buf2, sizeof(buf2)), 0, "strbufcasecmp four arguments");
288 
289 	T_ASSERT_NE(strbufcpy(buf, buf2), NULL, "strbufcpy two arguments");
290 	T_ASSERT_NE(strbufcpy(buf, sizeof(buf), buf2, sizeof(buf2)), NULL, "strbufcpy four arguments");
291 
292 	T_ASSERT_NE(strbufcat(buf, buf2), NULL, "strbufcat two arguments");
293 	T_ASSERT_NE(strbufcat(buf, sizeof(buf), buf2, sizeof(buf2)), NULL, "strbufcat four arguments");
294 }
295