1 /*
2 * test-client.c
3 * Tests for the public libpkgconf client 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 void
test_client_new_and_free(void)21 test_client_new_and_free(void)
22 {
23 pkgconf_cross_personality_t *pers = pkgconf_cross_personality_default();
24 pkgconf_client_t *client = pkgconf_client_new(NULL, NULL, pers, NULL, NULL);
25 TEST_ASSERT_NONNULL(client);
26
27 pkgconf_client_free(client);
28 }
29
30 static void
test_client_init_and_deinit_stack(void)31 test_client_init_and_deinit_stack(void)
32 {
33 /* Caller-allocated client, the path the CLI itself uses
34 * (pkgconf_cli_state_t embeds a pkgconf_client_t by value) */
35 pkgconf_client_t client = { 0 };
36 pkgconf_cross_personality_t *pers = pkgconf_cross_personality_default();
37
38 pkgconf_client_init(&client, NULL, NULL, pers, NULL, NULL);
39 pkgconf_client_deinit(&client);
40 }
41
42 static void
test_client_sysroot_dir(void)43 test_client_sysroot_dir(void)
44 {
45 pkgconf_client_t *client = test_client_new();
46
47 pkgconf_client_set_sysroot_dir(client, "/tmp/sysroot");
48 TEST_ASSERT_STRCMP_EQ(pkgconf_client_get_sysroot_dir(client), "/tmp/sysroot");
49
50 pkgconf_client_set_sysroot_dir(client, "/opt/sysroot");
51 TEST_ASSERT_STRCMP_EQ(pkgconf_client_get_sysroot_dir(client), "/opt/sysroot");
52
53 pkgconf_client_free(client);
54 }
55
56 static void
test_client_buildroot_dir(void)57 test_client_buildroot_dir(void)
58 {
59 pkgconf_client_t *client = test_client_new();
60
61 pkgconf_client_set_buildroot_dir(client, "/tmp/buildroot");
62 TEST_ASSERT_STRCMP_EQ(pkgconf_client_get_buildroot_dir(client), "/tmp/buildroot");
63
64 pkgconf_client_free(client);
65 }
66
67 static void
test_client_flags(void)68 test_client_flags(void)
69 {
70 pkgconf_client_t *client = test_client_new();
71
72 pkgconf_client_set_flags(client, PKGCONF_PKG_PKGF_NO_CACHE | PKGCONF_PKG_PKGF_SKIP_CONFLICTS);
73
74 unsigned int flags = pkgconf_client_get_flags(client);
75 TEST_ASSERT_NE(flags & PKGCONF_PKG_PKGF_NO_CACHE, 0);
76 TEST_ASSERT_NE(flags & PKGCONF_PKG_PKGF_SKIP_CONFLICTS, 0);
77 TEST_ASSERT_EQ(flags & PKGCONF_PKG_PKGF_ENV_ONLY, 0);
78
79 pkgconf_client_free(client);
80 }
81
82 static void
test_client_prefix_varname(void)83 test_client_prefix_varname(void)
84 {
85 pkgconf_client_t *client = test_client_new();
86
87 pkgconf_client_set_prefix_varname(client, "prefix");
88 TEST_ASSERT_STRCMP_EQ(pkgconf_client_get_prefix_varname(client), "prefix");
89
90 pkgconf_client_set_prefix_varname(client, "custom_prefix");
91 TEST_ASSERT_STRCMP_EQ(pkgconf_client_get_prefix_varname(client), "custom_prefix");
92
93 pkgconf_client_free(client);
94 }
95
96 /*
97 * Capture buffer for handler tests. Each handler writes the message
98 * into this buffer so the test can verify it fired with the expected
99 * content. Reset before each test that uses it.
100 */
101 static char capture_buf[256];
102
103 static bool
capture_handler(const char * msg,const pkgconf_client_t * client,void * data)104 capture_handler(const char *msg, const pkgconf_client_t *client, void *data)
105 {
106 (void) client;
107 (void) data;
108
109 strncpy(capture_buf, msg, sizeof(capture_buf) - 1);
110 capture_buf[sizeof(capture_buf) - 1] = '\0';
111 return true;
112 }
113
114 static void
capture_reset(void)115 capture_reset(void)
116 {
117 memset(capture_buf, 0, sizeof(capture_buf));
118 }
119
120 static void
test_client_error_handler_fires(void)121 test_client_error_handler_fires(void)
122 {
123 pkgconf_client_t *client = test_client_new();
124 capture_reset();
125
126 pkgconf_client_set_error_handler(client, capture_handler, NULL);
127
128 TEST_ASSERT_EQ(pkgconf_client_get_error_handler(client), capture_handler);
129
130 pkgconf_error(client, "test error: %d", 42);
131
132 TEST_ASSERT_NE(capture_buf[0], '\0');
133 TEST_ASSERT_STRSTR(capture_buf, "test error: 42");
134
135 pkgconf_client_free(client);
136 }
137
138 static void
test_client_warn_handler_fires(void)139 test_client_warn_handler_fires(void)
140 {
141 pkgconf_client_t *client = test_client_new();
142 capture_reset();
143
144 pkgconf_client_set_warn_handler(client, capture_handler, NULL);
145
146 TEST_ASSERT_EQ(pkgconf_client_get_warn_handler(client), capture_handler);
147
148 pkgconf_warn(client, "test warning: %s", "hello");
149
150 TEST_ASSERT_NE(capture_buf[0], '\0');
151 TEST_ASSERT_STRSTR(capture_buf, "test warning: hello");
152
153 pkgconf_client_free(client);
154 }
155
156 #ifndef PKGCONF_LITE
157 static void
test_client_trace_handler_fires(void)158 test_client_trace_handler_fires(void)
159 {
160 pkgconf_client_t *client = test_client_new();
161 capture_reset();
162
163 pkgconf_client_set_trace_handler(client, capture_handler, NULL);
164
165 TEST_ASSERT_EQ(pkgconf_client_get_trace_handler(client), capture_handler);
166
167 PKGCONF_TRACE(client, "trace message: %d", 7);
168
169 TEST_ASSERT_NE(capture_buf[0], '\0');
170 TEST_ASSERT_STRSTR(capture_buf, "trace message: 7");
171
172 pkgconf_client_free(client);
173 }
174 #endif
175
176 static void
unveil_capture_handler(const pkgconf_client_t * client,const char * path,const char * permissions)177 unveil_capture_handler(const pkgconf_client_t *client, const char *path, const char *permissions)
178 {
179 (void) client;
180 (void) permissions;
181
182 if (path != NULL)
183 {
184 strncpy(capture_buf, path, sizeof(capture_buf) - 1);
185 capture_buf[sizeof(capture_buf) - 1] = '\0';
186 }
187 }
188
189 static void
test_client_unveil_handler_installation(void)190 test_client_unveil_handler_installation(void)
191 {
192 pkgconf_client_t *client = test_client_new();
193
194 pkgconf_client_set_unveil_handler(client, unveil_capture_handler);
195 TEST_ASSERT_EQ(pkgconf_client_get_unveil_handler(client), unveil_capture_handler);
196
197 pkgconf_client_free(client);
198 }
199
200 /*
201 * Custom environ handler that returns canned values for known keys
202 * and NULL otherwise.
203 */
204 static const char *
canned_environ_handler(const pkgconf_client_t * client,const char * key)205 canned_environ_handler(const pkgconf_client_t *client, const char *key)
206 {
207 (void) client;
208
209 if (!strcmp(key, "PKG_TEST_VAR"))
210 return "the_value";
211 if (!strcmp(key, "EMPTY_VAR"))
212 return "";
213 if (!strcmp(key, "PKG_CONFIG_SYSTEM_INCLUDE_PATH"))
214 return "/custom/include";
215 if (!strcmp(key, "PKG_CONFIG_SYSTEM_LIBRARY_PATH"))
216 return "/custom/lib";
217
218 return NULL;
219 }
220
221 static void
test_client_getenv_via_handler(void)222 test_client_getenv_via_handler(void)
223 {
224 pkgconf_cross_personality_t *pers = pkgconf_cross_personality_default();
225 pkgconf_client_t *client = pkgconf_client_new(NULL, NULL, pers, NULL, canned_environ_handler);
226 TEST_ASSERT_NONNULL(client);
227
228 const char *v = pkgconf_client_getenv(client, "PKG_TEST_VAR");
229 TEST_ASSERT_NONNULL(v);
230 TEST_ASSERT_STRCMP_EQ(v, "the_value");
231
232 v = pkgconf_client_getenv(client, "EMPTY_VAR");
233 TEST_ASSERT_NONNULL(v);
234 TEST_ASSERT_STRCMP_EQ(v, "");
235
236 v = pkgconf_client_getenv(client, "UNDEFINED_VAR");
237 TEST_ASSERT_NULL(v);
238
239 pkgconf_client_free(client);
240 }
241
242 static void
test_client_dir_list_build_smoke(void)243 test_client_dir_list_build_smoke(void)
244 {
245 pkgconf_cross_personality_t *pers = pkgconf_cross_personality_default();
246 pkgconf_client_t *client = pkgconf_client_new(NULL, NULL, pers, NULL, NULL);
247 TEST_ASSERT_NONNULL(client);
248
249 pkgconf_client_dir_list_build(client, pers);
250
251 /* The personality's default dir list comes from PKG_DEFAULT_PATH
252 * (set at compile time). After build, the client should have
253 * SOME directories registered; we don't assert specific paths
254 * since they vary by build configuration. */
255 TEST_ASSERT_NONNULL(client->dir_list.head);
256
257 pkgconf_client_free(client);
258 }
259
260 static void
test_client_init_system_paths_from_environ(void)261 test_client_init_system_paths_from_environ(void)
262 {
263 pkgconf_cross_personality_t *pers = pkgconf_cross_personality_default();
264 pkgconf_client_t *client = pkgconf_client_new(NULL, NULL, pers, NULL, canned_environ_handler);
265 TEST_ASSERT_NONNULL(client);
266
267 /* With PKG_CONFIG_SYSTEM_{INCLUDE,LIBRARY}_PATH set, init builds the filter dirs from the
268 * environment instead of copying the personality's defaults. */
269 TEST_ASSERT_TRUE(pkgconf_path_match_list("/custom/include", &client->filter_includedirs));
270 TEST_ASSERT_TRUE(pkgconf_path_match_list("/custom/lib", &client->filter_libdirs));
271
272 pkgconf_client_free(client);
273 }
274
275 static void
test_client_preload_from_environ(void)276 test_client_preload_from_environ(void)
277 {
278 pkgconf_cross_personality_t *pers = pkgconf_cross_personality_default();
279 pkgconf_client_t *client = pkgconf_client_new(NULL, NULL, pers, NULL, NULL);
280 TEST_ASSERT_NONNULL(client);
281
282 /* preload_from_environ reads the named var via getenv directly.
283 * Point it at a dir; preload_path will try to load .pc files from there.
284 * An empty/nonexistent dir is fine; we're exercising the split-and-iterate path, not asserting
285 * loads. */
286 setenv("PKG_TEST_PRELOAD", "/nonexistent/dir", 1);
287
288 pkgconf_client_preload_from_environ(client, "PKG_TEST_PRELOAD");
289
290 unsetenv("PKG_TEST_PRELOAD");
291 pkgconf_client_free(client);
292 }
293
294 #ifndef PKGCONF_LITE
295 static void
test_client_trace_null_client(void)296 test_client_trace_null_client(void)
297 {
298 TEST_ASSERT_FALSE(pkgconf_trace(NULL, "test.c", 42, "func", "msg %d", 42));
299 }
300 #endif
301
302 int
main(int argc,char * argv[])303 main(int argc, char *argv[])
304 {
305 (void) argc;
306 const char *basename = pkgconf_path_find_basename(argv[0]);
307
308 TEST_RUN(basename, test_client_new_and_free);
309 TEST_RUN(basename, test_client_init_and_deinit_stack);
310 TEST_RUN(basename, test_client_init_system_paths_from_environ);
311 TEST_RUN(basename, test_client_preload_from_environ);
312
313 TEST_RUN(basename, test_client_sysroot_dir);
314 TEST_RUN(basename, test_client_buildroot_dir);
315 TEST_RUN(basename, test_client_flags);
316 TEST_RUN(basename, test_client_prefix_varname);
317
318 TEST_RUN(basename, test_client_error_handler_fires);
319 TEST_RUN(basename, test_client_warn_handler_fires);
320 #ifndef PKGCONF_LITE
321 TEST_RUN(basename, test_client_trace_handler_fires);
322 #endif
323 TEST_RUN(basename, test_client_unveil_handler_installation);
324
325 TEST_RUN(basename, test_client_getenv_via_handler);
326
327 TEST_RUN(basename, test_client_dir_list_build_smoke);
328
329 #ifndef PKGCONF_LITE
330 TEST_RUN(basename, test_client_trace_null_client);
331 #endif
332
333 return EXIT_SUCCESS;
334 }
335