xref: /freebsd/lib/libc/tests/string/strcspn_test.c (revision 069ac184)
1 /*-
2  * Copyright (c) 2023 The FreeBSD Foundation
3  *
4  * This software was developed by Robert Clausecker <fuz@FreeBSD.org>
5  * under sponsorship from the FreeBSD Foundation.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ''AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE
27  */
28 
29 
30 #include <atf-c.h>
31 #include <assert.h>
32 #include <limits.h>
33 #include <stdbool.h>
34 #include <stddef.h>
35 #include <string.h>
36 
37 enum {
38 	MAXALIGN = 16,	/* test all offsets from this alignment */
39 	MAXBUF = 64,	/* test up to this buffer length */
40 };
41 
42 enum { NOMATCH, MATCH };
43 
44 #ifdef STRSPN
45 #define STRXSPN strspn
46 #else
47 #define STRXSPN strcspn
48 #endif
49 
50 static void
51 testcase(char *buf, size_t buflen, char *set, size_t setlen, int want_match)
52 {
53 	size_t i, outcome, expected;
54 
55 	assert(setlen < UCHAR_MAX - 2);
56 
57 	for (i = 0; i < buflen; i++)
58 #ifdef STRSPN
59 		buf[i] = UCHAR_MAX - i % (setlen > 0 ? setlen : 1);
60 #else /* strcspn */
61 		buf[i] = 1 + i % (UCHAR_MAX - setlen - 1);
62 #endif
63 
64 	buf[i] = '\0';
65 
66 	for (i = 0; i < setlen; i++)
67 		set[i] = UCHAR_MAX - i;
68 
69 	set[i] = '\0';
70 
71 #ifdef STRSPN
72 	if (setlen == 0)
73 		expected = 0;
74 	else if (want_match == MATCH && buflen > 0) {
75 		buf[buflen - 1] = 1;
76 		expected = buflen - 1;
77 	} else
78 		expected = buflen;
79 #else /* strcspn */
80 	if (want_match == MATCH && buflen > 0 && setlen > 0) {
81 		buf[buflen - 1] = UCHAR_MAX;
82 		expected = buflen - 1;
83 	} else
84 		expected = buflen;
85 #endif
86 
87 	outcome = STRXSPN(buf, set);
88 	ATF_CHECK_EQ_MSG(expected, outcome, "%s(%p[%zu], %p[%zu]) = %zu != %zu",
89 	    __XSTRING(STRXSPN), buf, buflen, set, setlen, outcome, expected);
90 }
91 
92 /* test set with all alignments and lengths of buf */
93 static void
94 test_buf_alignments(char *set, size_t setlen, int want_match)
95 {
96 	char buf[MAXALIGN + MAXBUF + 1];
97 	size_t i, j;
98 
99 	for (i = 0; i < MAXALIGN; i++)
100 		for (j = 0; j <= MAXBUF; j++)
101 			testcase(buf + i, j, set, setlen, want_match);
102 }
103 
104 /* test buf with all alignments and lengths of set */
105 static void
106 test_set_alignments(char *buf, size_t buflen, int want_match)
107 {
108 	char set[MAXALIGN + MAXBUF + 1];
109 	size_t i, j;
110 
111 	for (i = 0; i < MAXALIGN; i++)
112 		for (j = 0; j <= MAXBUF; j++)
113 			testcase(buf, buflen, set + i, j, want_match);
114 }
115 
116 ATF_TC_WITHOUT_HEAD(buf_alignments);
117 ATF_TC_BODY(buf_alignments, tc)
118 {
119 	char set[41];
120 
121 	test_buf_alignments(set, 0, MATCH);
122 	test_buf_alignments(set, 1, MATCH);
123 	test_buf_alignments(set, 5, MATCH);
124 	test_buf_alignments(set, 20, MATCH);
125 	test_buf_alignments(set, 40, MATCH);
126 
127 	test_buf_alignments(set, 0, NOMATCH);
128 	test_buf_alignments(set, 1, NOMATCH);
129 	test_buf_alignments(set, 5, NOMATCH);
130 	test_buf_alignments(set, 20, NOMATCH);
131 	test_buf_alignments(set, 40, NOMATCH);
132 }
133 
134 ATF_TC_WITHOUT_HEAD(set_alignments);
135 ATF_TC_BODY(set_alignments, tc)
136 {
137 	char buf[31];
138 
139 	test_set_alignments(buf,  0, MATCH);
140 	test_set_alignments(buf, 10, MATCH);
141 	test_set_alignments(buf, 20, MATCH);
142 	test_set_alignments(buf, 30, MATCH);
143 
144 	test_set_alignments(buf,  0, NOMATCH);
145 	test_set_alignments(buf, 10, NOMATCH);
146 	test_set_alignments(buf, 20, NOMATCH);
147 	test_set_alignments(buf, 30, NOMATCH);
148 }
149 
150 #ifndef STRSPN
151 /* test all positions in which set could match buf */
152 static void
153 test_match_positions(char *buf, char *set, size_t buflen, size_t setlen)
154 {
155 	size_t i, j, outcome;
156 
157 	memset(buf, '-', buflen);
158 
159 	for (i = 0; i < setlen; i++)
160 		set[i] = 'A' + i;
161 
162 	buf[buflen] = '\0';
163 	set[setlen] = '\0';
164 
165 	/*
166 	 * Check for (mis)match at buffer position i
167 	 * against set position j.
168 	 */
169 	for (i = 0; i < buflen; i++) {
170 		for (j = 0; j < setlen; j++) {
171 			buf[i] = set[j];
172 
173 			outcome = strcspn(buf, set);
174 			ATF_CHECK_EQ_MSG(i, outcome,
175 			    "strcspn(\"%s\", \"%s\") = %zu != %zu",
176 			    buf, set, outcome, i);
177 		}
178 
179 		buf[i] = '-';
180 	}
181 }
182 
183 ATF_TC_WITHOUT_HEAD(match_positions);
184 ATF_TC_BODY(match_positions, tc)
185 {
186 	char buf[129], set[65];
187 
188 	test_match_positions(buf, set, 128, 64);
189 	test_match_positions(buf, set, 64, 64);
190 	test_match_positions(buf, set, 32, 64);
191 	test_match_positions(buf, set, 16, 64);
192 	test_match_positions(buf, set, 8, 64);
193 	test_match_positions(buf, set, 128, 32);
194 	test_match_positions(buf, set, 64, 32);
195 	test_match_positions(buf, set, 32, 32);
196 	test_match_positions(buf, set, 16, 32);
197 	test_match_positions(buf, set, 8, 32);
198 	test_match_positions(buf, set, 128, 16);
199 	test_match_positions(buf, set, 64, 16);
200 	test_match_positions(buf, set, 32, 16);
201 	test_match_positions(buf, set, 16, 16);
202 	test_match_positions(buf, set, 8, 16);
203 	test_match_positions(buf, set, 128, 8);
204 	test_match_positions(buf, set, 64, 8);
205 	test_match_positions(buf, set, 32, 8);
206 	test_match_positions(buf, set, 16, 8);
207 	test_match_positions(buf, set, 8, 8);
208 }
209 
210 /* if there are two matches, check that the earlier match is taken */
211 static void
212 test_match_order(char *buf, char *set, size_t buflen, size_t setlen)
213 {
214 	size_t i, j, k, l, outcome;
215 
216 	memset(buf, '-', buflen);
217 
218 	for (i = 0; i < setlen; i++)
219 		set[i] = 'A' + i;
220 
221 	buf[buflen] = '\0';
222 	set[setlen] = '\0';
223 
224 	for (i = 0; i < setlen; i++)
225 		for (j = 0; j < setlen; j++)
226 			for (k = 0; k + 1 < buflen; k++)
227 				for (l = k + 1; l < buflen; l++) {
228 					buf[k] = set[i];
229 					buf[l] = set[j];
230 					outcome = strcspn(buf, set);
231 					ATF_CHECK_EQ_MSG(k, outcome,
232 					    "strcspn(\"%s\", \"%s\") = %zu != %zu",
233 					    buf, set, outcome, k);
234 					buf[k] = '-';
235 					buf[l] = '-';
236 				}
237 }
238 
239 ATF_TC_WITHOUT_HEAD(match_order);
240 ATF_TC_BODY(match_order, tc)
241 {
242 	char buf[33], set[65];
243 
244 	test_match_order(buf, set, 32, 64);
245 	test_match_order(buf, set, 16, 64);
246 	test_match_order(buf, set, 8, 64);
247 	test_match_order(buf, set, 32, 32);
248 	test_match_order(buf, set, 16, 32);
249 	test_match_order(buf, set, 8, 32);
250 	test_match_order(buf, set, 32, 16);
251 	test_match_order(buf, set, 16, 16);
252 	test_match_order(buf, set, 8, 16);
253 	test_match_order(buf, set, 32, 8);
254 	test_match_order(buf, set, 16, 8);
255 	test_match_order(buf, set, 8, 8);
256 }
257 #endif /* !defined(STRSPN) */
258 
259 ATF_TP_ADD_TCS(tp)
260 {
261 	ATF_TP_ADD_TC(tp, buf_alignments);
262 	ATF_TP_ADD_TC(tp, set_alignments);
263 #ifndef STRSPN
264 	ATF_TP_ADD_TC(tp, match_positions);
265 	ATF_TP_ADD_TC(tp, match_order);
266 #endif
267 
268 	return (atf_no_error());
269 }
270