xref: /freebsd/contrib/pkgconf/tests/api/test-variable.c (revision 592efe252472a3385acf36b1f49ecf710a7f3d9c)
1 /*
2  * test-variable.c
3  * Tests for the public libpkgconf variable API.
4  *
5  * SPDX-License-Identifier: pkgconf
6  *
7  * Copyright (c) 2025 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 <libpkgconf/stdinc.h>
19 #include <libpkgconf/libpkgconf.h>
20 #include "test-api.h"
21 
22 /*
23  * Install a value on a variable by compiling it as bytecode.
24  * Mirrors what the parser does when it reads a .pc field.
25  */
26 static void
seed_variable(pkgconf_list_t * vars,const char * key,const char * value)27 seed_variable(pkgconf_list_t *vars, const char *key, const char *value)
28 {
29 	pkgconf_variable_t *v = pkgconf_variable_get_or_create(vars, key);
30 	TEST_ASSERT_NONNULL(v);
31 
32 	pkgconf_buffer_reset(&v->bcbuf);
33 	pkgconf_bytecode_compile(&v->bcbuf, value);
34 	pkgconf_bytecode_from_buffer(&v->bc, &v->bcbuf);
35 }
36 
37 static void
test_variable_new_and_free(void)38 test_variable_new_and_free(void)
39 {
40 	pkgconf_variable_t *v = pkgconf_variable_new("prefix");
41 
42 	TEST_ASSERT_NONNULL(v);
43 	TEST_ASSERT_STRCMP_EQ(v->key, "prefix");
44 
45 	pkgconf_variable_free(v);
46 }
47 
48 static void
test_variable_get_or_create_creates(void)49 test_variable_get_or_create_creates(void)
50 {
51 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
52 
53 	pkgconf_variable_t *v = pkgconf_variable_get_or_create(&vars, "prefix");
54 	TEST_ASSERT_NONNULL(v);
55 	TEST_ASSERT_STRCMP_EQ(v->key, "prefix");
56 
57 	pkgconf_variable_list_free(&vars);
58 }
59 
60 static void
test_variable_get_or_create_returns_existing(void)61 test_variable_get_or_create_returns_existing(void)
62 {
63 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
64 
65 	pkgconf_variable_t *first = pkgconf_variable_get_or_create(&vars, "libdir");
66 	TEST_ASSERT_NONNULL(first);
67 
68 	// A second call with the same key should return the same object, not create a duplicate.
69 	pkgconf_variable_t *second = pkgconf_variable_get_or_create(&vars, "libdir");
70 	TEST_ASSERT_NONNULL(second);
71 	TEST_ASSERT_EQ(first, second);
72 
73 	pkgconf_variable_list_free(&vars);
74 }
75 
76 static void
test_variable_find_present(void)77 test_variable_find_present(void)
78 {
79 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
80 
81 	pkgconf_variable_t *created = pkgconf_variable_get_or_create(&vars, "prefix");
82 	TEST_ASSERT_NONNULL(created);
83 
84 	pkgconf_variable_t *found = pkgconf_variable_find(&vars, "prefix");
85 	TEST_ASSERT_NONNULL(found);
86 	TEST_ASSERT_EQ(created, found);
87 
88 	pkgconf_variable_list_free(&vars);
89 }
90 
91 static void
test_variable_find_absent(void)92 test_variable_find_absent(void)
93 {
94 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
95 
96 	pkgconf_variable_t *found = pkgconf_variable_find(&vars, "nonexistent");
97 	TEST_ASSERT_NULL(found);
98 
99 	pkgconf_variable_list_free(&vars);
100 }
101 
102 static void
test_variable_find_among_many(void)103 test_variable_find_among_many(void)
104 {
105 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
106 
107 	pkgconf_variable_get_or_create(&vars, "prefix");
108 	pkgconf_variable_get_or_create(&vars, "exec_prefix");
109 	pkgconf_variable_get_or_create(&vars, "libdir");
110 	pkgconf_variable_get_or_create(&vars, "includedir");
111 
112 	TEST_ASSERT_NONNULL(pkgconf_variable_find(&vars, "prefix"));
113 	TEST_ASSERT_NONNULL(pkgconf_variable_find(&vars, "exec_prefix"));
114 	TEST_ASSERT_NONNULL(pkgconf_variable_find(&vars, "libdir"));
115 	TEST_ASSERT_NONNULL(pkgconf_variable_find(&vars, "includedir"));
116 	TEST_ASSERT_NULL(pkgconf_variable_find(&vars, "notpresent"));
117 
118 	pkgconf_variable_list_free(&vars);
119 }
120 
121 static void
test_variable_delete(void)122 test_variable_delete(void)
123 {
124 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
125 
126 	pkgconf_variable_t *v = pkgconf_variable_get_or_create(&vars, "prefix");
127 	TEST_ASSERT_NONNULL(v);
128 	TEST_ASSERT_NONNULL(pkgconf_variable_find(&vars, "prefix"));
129 
130 	pkgconf_variable_delete(&vars, v);
131 	TEST_ASSERT_NULL(pkgconf_variable_find(&vars, "prefix"));
132 
133 	pkgconf_variable_list_free(&vars);
134 }
135 
136 static void
test_variable_eval_str_plain(void)137 test_variable_eval_str_plain(void)
138 {
139 	pkgconf_client_t *client = test_client_new();
140 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
141 	bool saw_sysroot = false;
142 
143 	seed_variable(&vars, "version", "1.2.3");
144 
145 	pkgconf_variable_t *v = pkgconf_variable_find(&vars, "version");
146 	TEST_ASSERT_NONNULL(v);
147 
148 	char *out = pkgconf_variable_eval_str(client, &vars, v, &saw_sysroot);
149 	TEST_ASSERT_NONNULL(out);
150 	TEST_ASSERT_STRCMP_EQ(out, "1.2.3");
151 
152 	free(out);
153 	pkgconf_variable_list_free(&vars);
154 	pkgconf_client_free(client);
155 }
156 
157 static void
test_variable_eval_str_with_reference(void)158 test_variable_eval_str_with_reference(void)
159 {
160 	pkgconf_client_t *client = test_client_new();
161 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
162 	bool saw_sysroot = false;
163 
164 	// Standard pkg-config layout: libdir is derived from prefix.
165 	seed_variable(&vars, "prefix", "/opt/foo");
166 	seed_variable(&vars, "libdir", "${prefix}/lib");
167 
168 	pkgconf_variable_t *libdir = pkgconf_variable_find(&vars, "libdir");
169 	TEST_ASSERT_NONNULL(libdir);
170 
171 	char *out = pkgconf_variable_eval_str(client, &vars, libdir, &saw_sysroot);
172 	TEST_ASSERT_NONNULL(out);
173 	TEST_ASSERT_STRCMP_EQ(out, "/opt/foo/lib");
174 
175 	free(out);
176 	pkgconf_variable_list_free(&vars);
177 	pkgconf_client_free(client);
178 }
179 
180 static void
test_variable_eval_str_chained(void)181 test_variable_eval_str_chained(void)
182 {
183 	pkgconf_client_t *client = test_client_new();
184 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
185 	bool saw_sysroot = false;
186 
187 	seed_variable(&vars, "prefix", "/usr/local");
188 	seed_variable(&vars, "exec_prefix", "${prefix}");
189 	seed_variable(&vars, "includedir", "${exec_prefix}/include");
190 
191 	pkgconf_variable_t *includedir = pkgconf_variable_find(&vars, "includedir");
192 	TEST_ASSERT_NONNULL(includedir);
193 
194 	char *out = pkgconf_variable_eval_str(client, &vars, includedir, &saw_sysroot);
195 	TEST_ASSERT_NONNULL(out);
196 	TEST_ASSERT_STRCMP_EQ(out, "/usr/local/include");
197 
198 	free(out);
199 	pkgconf_variable_list_free(&vars);
200 	pkgconf_client_free(client);
201 }
202 
203 static void
test_variable_list_free_handles_empty(void)204 test_variable_list_free_handles_empty(void)
205 {
206 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
207 
208 	// Freeing an empty list should be a no-op and not crash. Mostly an ASan/leak smoke test.
209 	pkgconf_variable_list_free(&vars);
210 }
211 
212 static void
test_variable_list_free_handles_many(void)213 test_variable_list_free_handles_many(void)
214 {
215 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
216 
217 	/* Add a mix of bare and value-carrying variables, then free the whole list at once.
218 	 * Mostly an ASan/leak smoke test. */
219 	pkgconf_variable_get_or_create(&vars, "a");
220 	seed_variable(&vars, "b", "/path/b");
221 	pkgconf_variable_get_or_create(&vars, "c");
222 	seed_variable(&vars, "d", "${b}/sub");
223 
224 	pkgconf_variable_list_free(&vars);
225 }
226 
227 int
main(int argc,char * argv[])228 main(int argc, char *argv[])
229 {
230 	(void) argc;
231 	const char *basename = pkgconf_path_find_basename(argv[0]);
232 
233 	TEST_RUN(basename, test_variable_new_and_free);
234 	TEST_RUN(basename, test_variable_get_or_create_creates);
235 	TEST_RUN(basename, test_variable_get_or_create_returns_existing);
236 	TEST_RUN(basename, test_variable_find_present);
237 	TEST_RUN(basename, test_variable_find_absent);
238 	TEST_RUN(basename, test_variable_find_among_many);
239 	TEST_RUN(basename, test_variable_delete);
240 	TEST_RUN(basename, test_variable_eval_str_plain);
241 	TEST_RUN(basename, test_variable_eval_str_with_reference);
242 	TEST_RUN(basename, test_variable_eval_str_chained);
243 	TEST_RUN(basename, test_variable_list_free_handles_empty);
244 	TEST_RUN(basename, test_variable_list_free_handles_many);
245 
246 	return EXIT_SUCCESS;
247 }
248