xref: /freebsd/contrib/pkgconf/tests/api/test-fragment.c (revision 592efe252472a3385acf36b1f49ecf710a7f3d9c)
1 /*
2  * test-fragment.c
3  * Tests for the public libpkgconf fragment API.
4  *
5  * SPDX-License-Identifier: pkgconf
6  *
7  * Copyright (c) 2026 pkgconf authors (see AUTHORS).
8  *
9  * Permission to use, copy, modify, and/or distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * This software is provided 'as is' and without any warranty, express or
14  * implied.  In no event shall the authors be liable for any damages arising
15  * from the use of this software.
16  */
17 
18 #include "test-api.h"
19 
20 static size_t
fragment_count(const pkgconf_list_t * list)21 fragment_count(const pkgconf_list_t *list)
22 {
23 	size_t n = 0;
24 	const pkgconf_node_t *iter;
25 
26 	PKGCONF_FOREACH_LIST_ENTRY(list->head, iter)
27 	{
28 		n++;
29 	}
30 
31 	return n;
32 }
33 
34 static const pkgconf_fragment_t *
fragment_at(const pkgconf_list_t * list,size_t index)35 fragment_at(const pkgconf_list_t *list, size_t index)
36 {
37 	const pkgconf_node_t *iter;
38 	size_t i = 0;
39 
40 	PKGCONF_FOREACH_LIST_ENTRY(list->head, iter)
41 	{
42 		if (i++ == index)
43 			return iter->data;
44 	}
45 
46 	return NULL;
47 }
48 
49 /*
50  * Render a fragment list to a newly-allocated C string for assertions.
51  * Caller frees.
52  */
53 static char *
render_to_string(const pkgconf_list_t * list)54 render_to_string(const pkgconf_list_t *list)
55 {
56 	pkgconf_buffer_t buf = PKGCONF_BUFFER_INITIALIZER;
57 	pkgconf_fragment_render_buf(list, &buf, false, NULL, ' ');
58 
59 	if (pkgconf_buffer_str(&buf) == NULL)
60 		return strdup("");
61 
62 	char *out = strdup(pkgconf_buffer_str(&buf));
63 	pkgconf_buffer_finalize(&buf);
64 	return out;
65 }
66 
67 static void
test_fragment_parse_cflags(void)68 test_fragment_parse_cflags(void)
69 {
70 	pkgconf_client_t *client = test_client_new();
71 	pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER;
72 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
73 
74 	TEST_ASSERT_TRUE(pkgconf_fragment_parse(client, &frags, &vars, "-I/usr/include -DFOO=1", 0));
75 	TEST_ASSERT_EQ(fragment_count(&frags), 2);
76 
77 	const pkgconf_fragment_t *first = fragment_at(&frags, 0);
78 	TEST_ASSERT_NONNULL(first);
79 	TEST_ASSERT_EQ(first->type, 'I');
80 	TEST_ASSERT_STRCMP_EQ(first->data, "/usr/include");
81 
82 	pkgconf_fragment_free(&frags);
83 	pkgconf_variable_list_free(&vars);
84 	pkgconf_client_free(client);
85 }
86 
87 static void
test_fragment_parse_libs(void)88 test_fragment_parse_libs(void)
89 {
90 	pkgconf_client_t *client = test_client_new();
91 	pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER;
92 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
93 
94 	TEST_ASSERT_TRUE(pkgconf_fragment_parse(client, &frags, &vars, "-L/usr/lib -lfoo -lbar", 0));
95 	TEST_ASSERT_EQ(fragment_count(&frags), 3);
96 
97 	const pkgconf_fragment_t *f0 = fragment_at(&frags, 0);
98 	const pkgconf_fragment_t *f1 = fragment_at(&frags, 1);
99 	const pkgconf_fragment_t *f2 = fragment_at(&frags, 2);
100 
101 	TEST_ASSERT_NONNULL(f0);
102 	TEST_ASSERT_NONNULL(f1);
103 	TEST_ASSERT_NONNULL(f2);
104 
105 	TEST_ASSERT_EQ(f0->type, 'L');
106 	TEST_ASSERT_STRCMP_EQ(f0->data, "/usr/lib");
107 	TEST_ASSERT_EQ(f1->type, 'l');
108 	TEST_ASSERT_STRCMP_EQ(f1->data, "foo");
109 	TEST_ASSERT_EQ(f2->type, 'l');
110 	TEST_ASSERT_STRCMP_EQ(f2->data, "bar");
111 
112 	pkgconf_fragment_free(&frags);
113 	pkgconf_variable_list_free(&vars);
114 	pkgconf_client_free(client);
115 }
116 
117 static void
test_fragment_parse_empty(void)118 test_fragment_parse_empty(void)
119 {
120 	pkgconf_client_t *client = test_client_new();
121 	pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER;
122 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
123 
124 	TEST_ASSERT_TRUE(pkgconf_fragment_parse(client, &frags, &vars, "", 0));
125 	TEST_ASSERT_EQ(fragment_count(&frags), 0);
126 
127 	pkgconf_fragment_free(&frags);
128 	pkgconf_variable_list_free(&vars);
129 	pkgconf_client_free(client);
130 }
131 
132 static void
test_fragment_add_single(void)133 test_fragment_add_single(void)
134 {
135 	pkgconf_client_t *client = test_client_new();
136 	pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER;
137 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
138 
139 	pkgconf_fragment_add(client, &frags, &vars, "-I/opt/include", 0);
140 
141 	TEST_ASSERT_EQ(fragment_count(&frags), 1);
142 
143 	const pkgconf_fragment_t *f = fragment_at(&frags, 0);
144 	TEST_ASSERT_NONNULL(f);
145 	TEST_ASSERT_EQ(f->type, 'I');
146 	TEST_ASSERT_STRCMP_EQ(f->data, "/opt/include");
147 
148 	pkgconf_fragment_free(&frags);
149 	pkgconf_variable_list_free(&vars);
150 	pkgconf_client_free(client);
151 }
152 
153 static void
test_fragment_render_cflags(void)154 test_fragment_render_cflags(void)
155 {
156 	pkgconf_client_t *client = test_client_new();
157 	pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER;
158 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
159 
160 	pkgconf_fragment_parse(client, &frags, &vars, "-I/usr/include -I/opt/include", 0);
161 
162 	char *rendered = render_to_string(&frags);
163 	TEST_ASSERT_NONNULL(rendered);
164 	TEST_ASSERT_STRCMP_EQ(rendered, "-I/usr/include -I/opt/include");
165 
166 	free(rendered);
167 	pkgconf_fragment_free(&frags);
168 	pkgconf_variable_list_free(&vars);
169 	pkgconf_client_free(client);
170 }
171 
172 static void
test_fragment_render_libs(void)173 test_fragment_render_libs(void)
174 {
175 	pkgconf_client_t *client = test_client_new();
176 	pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER;
177 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
178 
179 	pkgconf_fragment_parse(client, &frags, &vars, "-L/usr/lib -lfoo", 0);
180 
181 	char *rendered = render_to_string(&frags);
182 	TEST_ASSERT_NONNULL(rendered);
183 	TEST_ASSERT_STRCMP_EQ(rendered, "-L/usr/lib -lfoo");
184 
185 	free(rendered);
186 	pkgconf_fragment_free(&frags);
187 	pkgconf_variable_list_free(&vars);
188 	pkgconf_client_free(client);
189 }
190 
191 static void
test_fragment_render_empty(void)192 test_fragment_render_empty(void)
193 {
194 	pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER;
195 
196 	char *rendered = render_to_string(&frags);
197 	TEST_ASSERT_NONNULL(rendered);
198 	TEST_ASSERT_EMPTY_STRING(rendered);
199 
200 	free(rendered);
201 }
202 
203 // Filter predicate: keep only -I (include) fragments.
204 static bool
filter_only_includes(const pkgconf_client_t * client,const pkgconf_fragment_t * frag,void * data)205 filter_only_includes(const pkgconf_client_t *client, const pkgconf_fragment_t *frag, void *data)
206 {
207 	(void) client;
208 	(void) data;
209 	return frag->type == 'I';
210 }
211 
212 // Filter predicate: keep only -l (library name) fragments.
213 static bool
filter_only_libnames(const pkgconf_client_t * client,const pkgconf_fragment_t * frag,void * data)214 filter_only_libnames(const pkgconf_client_t *client, const pkgconf_fragment_t *frag, void *data)
215 {
216 	(void) client;
217 	(void) data;
218 	return frag->type == 'l';
219 }
220 
221 // Filter predicate: keep nothing.
222 static bool
filter_nothing(const pkgconf_client_t * client,const pkgconf_fragment_t * frag,void * data)223 filter_nothing(const pkgconf_client_t *client, const pkgconf_fragment_t *frag, void *data)
224 {
225 	(void) client;
226 	(void) frag;
227 	(void) data;
228 	return false;
229 }
230 
231 static void
test_fragment_filter_only_includes(void)232 test_fragment_filter_only_includes(void)
233 {
234 	pkgconf_client_t *client = test_client_new();
235 	pkgconf_list_t src = PKGCONF_LIST_INITIALIZER;
236 	pkgconf_list_t dst = PKGCONF_LIST_INITIALIZER;
237 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
238 
239 	pkgconf_fragment_parse(client, &src, &vars, "-I/usr/include -L/usr/lib -lfoo -I/opt/include", 0);
240 
241 	pkgconf_fragment_filter(client, &dst, &src, filter_only_includes, NULL);
242 	TEST_ASSERT_EQ(fragment_count(&dst), 2);
243 
244 	const pkgconf_fragment_t *f0 = fragment_at(&dst, 0);
245 	const pkgconf_fragment_t *f1 = fragment_at(&dst, 1);
246 	TEST_ASSERT_NONNULL(f0);
247 	TEST_ASSERT_NONNULL(f1);
248 	TEST_ASSERT_EQ(f0->type, 'I');
249 	TEST_ASSERT_STRCMP_EQ(f0->data, "/usr/include");
250 	TEST_ASSERT_EQ(f1->type, 'I');
251 	TEST_ASSERT_STRCMP_EQ(f1->data, "/opt/include");
252 
253 	pkgconf_fragment_free(&dst);
254 	pkgconf_fragment_free(&src);
255 	pkgconf_variable_list_free(&vars);
256 	pkgconf_client_free(client);
257 }
258 
259 static void
test_fragment_filter_only_libnames(void)260 test_fragment_filter_only_libnames(void)
261 {
262 	pkgconf_client_t *client = test_client_new();
263 	pkgconf_list_t src = PKGCONF_LIST_INITIALIZER;
264 	pkgconf_list_t dst = PKGCONF_LIST_INITIALIZER;
265 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
266 
267 	pkgconf_fragment_parse(client, &src, &vars, "-L/usr/lib -lfoo -lbar -I/usr/include", 0);
268 
269 	pkgconf_fragment_filter(client, &dst, &src, filter_only_libnames, NULL);
270 
271 	TEST_ASSERT_EQ(fragment_count(&dst), 2);
272 
273 	const pkgconf_fragment_t *f0 = fragment_at(&dst, 0);
274 	const pkgconf_fragment_t *f1 = fragment_at(&dst, 1);
275 	TEST_ASSERT_NONNULL(f0);
276 	TEST_ASSERT_NONNULL(f1);
277 	TEST_ASSERT_EQ(f0->type, 'l');
278 	TEST_ASSERT_STRCMP_EQ(f0->data, "foo");
279 	TEST_ASSERT_EQ(f1->type, 'l');
280 	TEST_ASSERT_STRCMP_EQ(f1->data, "bar");
281 
282 	pkgconf_fragment_free(&dst);
283 	pkgconf_fragment_free(&src);
284 	pkgconf_variable_list_free(&vars);
285 	pkgconf_client_free(client);
286 }
287 
288 static void
test_fragment_filter_keeps_nothing(void)289 test_fragment_filter_keeps_nothing(void)
290 {
291 	pkgconf_client_t *client = test_client_new();
292 	pkgconf_list_t src = PKGCONF_LIST_INITIALIZER;
293 	pkgconf_list_t dst = PKGCONF_LIST_INITIALIZER;
294 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
295 
296 	pkgconf_fragment_parse(client, &src, &vars, "-I/usr/include -lfoo", 0);
297 
298 	pkgconf_fragment_filter(client, &dst, &src, filter_nothing, NULL);
299 
300 	TEST_ASSERT_EQ(fragment_count(&dst), 0);
301 
302 	pkgconf_fragment_free(&dst);
303 	pkgconf_fragment_free(&src);
304 	pkgconf_variable_list_free(&vars);
305 	pkgconf_client_free(client);
306 }
307 
308 static void
test_fragment_has_system_dir_matches(void)309 test_fragment_has_system_dir_matches(void)
310 {
311 	pkgconf_client_t *client = test_client_new();
312 	pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER;
313 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
314 
315 	pkgconf_path_add("/usr/include", &client->filter_includedirs, false);
316 
317 	pkgconf_fragment_parse(client, &frags, &vars, "-I/usr/include -I/opt/include", 0);
318 
319 	const pkgconf_fragment_t *system = fragment_at(&frags, 0);
320 	const pkgconf_fragment_t *other = fragment_at(&frags, 1);
321 	TEST_ASSERT_NONNULL(system);
322 	TEST_ASSERT_NONNULL(other);
323 
324 	TEST_ASSERT_TRUE(pkgconf_fragment_has_system_dir(client, system));
325 	TEST_ASSERT_FALSE(pkgconf_fragment_has_system_dir(client, other));
326 
327 	pkgconf_fragment_free(&frags);
328 	pkgconf_variable_list_free(&vars);
329 	pkgconf_client_free(client);
330 }
331 
332 static void
test_fragment_has_system_dir_libs(void)333 test_fragment_has_system_dir_libs(void)
334 {
335 	pkgconf_client_t *client = test_client_new();
336 	pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER;
337 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
338 
339 	pkgconf_path_add("/usr/lib", &client->filter_libdirs, false);
340 
341 	pkgconf_fragment_parse(client, &frags, &vars, "-L/usr/lib -L/opt/lib", 0);
342 
343 	const pkgconf_fragment_t *system = fragment_at(&frags, 0);
344 	const pkgconf_fragment_t *other = fragment_at(&frags, 1);
345 	TEST_ASSERT_NONNULL(system);
346 	TEST_ASSERT_NONNULL(other);
347 
348 	TEST_ASSERT_TRUE(pkgconf_fragment_has_system_dir(client, system));
349 	TEST_ASSERT_FALSE(pkgconf_fragment_has_system_dir(client, other));
350 
351 	pkgconf_fragment_free(&frags);
352 	pkgconf_variable_list_free(&vars);
353 	pkgconf_client_free(client);
354 }
355 
356 int
main(int argc,char * argv[])357 main(int argc, char *argv[])
358 {
359 	(void) argc;
360 	const char *basename = pkgconf_path_find_basename(argv[0]);
361 
362 	TEST_RUN(basename, test_fragment_parse_empty);
363 	TEST_RUN(basename, test_fragment_parse_cflags);
364 	TEST_RUN(basename, test_fragment_parse_libs);
365 	TEST_RUN(basename, test_fragment_add_single);
366 	TEST_RUN(basename, test_fragment_render_empty);
367 	TEST_RUN(basename, test_fragment_render_cflags);
368 	TEST_RUN(basename, test_fragment_render_libs);
369 	TEST_RUN(basename, test_fragment_filter_only_includes);
370 	TEST_RUN(basename, test_fragment_filter_only_libnames);
371 	TEST_RUN(basename, test_fragment_filter_keeps_nothing);
372 	TEST_RUN(basename, test_fragment_has_system_dir_matches);
373 	TEST_RUN(basename, test_fragment_has_system_dir_libs);
374 
375 	return EXIT_SUCCESS;
376 }
377