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