xref: /freebsd/contrib/pkgconf/tests/api/test-tuple.c (revision 592efe252472a3385acf36b1f49ecf710a7f3d9c)
1 /*
2  * test-tuple.c
3  * Tests for the public libpkgconf tuple 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 static void
test_tuple_add_and_find(void)23 test_tuple_add_and_find(void)
24 {
25 	pkgconf_client_t *client = test_client_new();
26 	pkgconf_list_t tuples = PKGCONF_LIST_INITIALIZER;
27 
28 	/* parse=false means store the value verbatim, no bytecode compile.
29 	 * That's the right mode for storing already-evaluated values. */
30 	pkgconf_tuple_t *t = pkgconf_tuple_add(client, &tuples, "prefix", "/opt/foo", false, 0);
31 	TEST_ASSERT_NONNULL(t);
32 
33 	const char *found = pkgconf_tuple_find(client, &tuples, "prefix");
34 	TEST_ASSERT_NONNULL(found);
35 	TEST_ASSERT_STRCMP_EQ(found, "/opt/foo");
36 
37 	pkgconf_tuple_free(&tuples);
38 	pkgconf_client_free(client);
39 }
40 
41 static void
test_tuple_find_absent(void)42 test_tuple_find_absent(void)
43 {
44 	pkgconf_client_t *client = test_client_new();
45 	pkgconf_list_t tuples = PKGCONF_LIST_INITIALIZER;
46 
47 	const char *found = pkgconf_tuple_find(client, &tuples, "nonexistent");
48 	TEST_ASSERT_EMPTY_STRING(found);
49 
50 	pkgconf_tuple_free(&tuples);
51 	pkgconf_client_free(client);
52 }
53 
54 static void
test_tuple_add_multiple(void)55 test_tuple_add_multiple(void)
56 {
57 	pkgconf_client_t *client = test_client_new();
58 	pkgconf_list_t tuples = PKGCONF_LIST_INITIALIZER;
59 
60 	pkgconf_tuple_add(client, &tuples, "prefix", "/usr", false, 0);
61 	pkgconf_tuple_add(client, &tuples, "exec_prefix", "/usr/local", false, 0);
62 	pkgconf_tuple_add(client, &tuples, "libdir", "/usr/lib", false, 0);
63 
64 	TEST_ASSERT_STRCMP_EQ(pkgconf_tuple_find(client, &tuples, "prefix"), "/usr");
65 	TEST_ASSERT_STRCMP_EQ(pkgconf_tuple_find(client, &tuples, "exec_prefix"), "/usr/local");
66 	TEST_ASSERT_STRCMP_EQ(pkgconf_tuple_find(client, &tuples, "libdir"), "/usr/lib");
67 	TEST_ASSERT_EMPTY_STRING(pkgconf_tuple_find(client, &tuples, "datadir"));
68 
69 	pkgconf_tuple_free(&tuples);
70 	pkgconf_client_free(client);
71 }
72 
73 static void
test_tuple_global_add_and_find(void)74 test_tuple_global_add_and_find(void)
75 {
76 	pkgconf_client_t *client = test_client_new();
77 
78 	pkgconf_tuple_add_global(client, "PKG_TEST_KEY", "test_value");
79 
80 	const char *found = pkgconf_tuple_find_global(client, "PKG_TEST_KEY");
81 	TEST_ASSERT_NONNULL(found);
82 	TEST_ASSERT_STRCMP_EQ(found, "test_value");
83 
84 	pkgconf_tuple_free_global(client);
85 	pkgconf_client_free(client);
86 }
87 
88 static void
test_tuple_global_find_absent(void)89 test_tuple_global_find_absent(void)
90 {
91 	pkgconf_client_t *client = test_client_new();
92 
93 	const char *found = pkgconf_tuple_find_global(client, "DOES_NOT_EXIST");
94 	TEST_ASSERT_EMPTY_STRING(found);
95 
96 	pkgconf_client_free(client);
97 }
98 
99 static void
test_tuple_define_global_kv_form(void)100 test_tuple_define_global_kv_form(void)
101 {
102 	pkgconf_client_t *client = test_client_new();
103 
104 	/* This is the exact path --define-variable=KEY=VALUE takes
105 	 * pkgconf_tuple_define_global parses the "key=value" form and inserts it as a global tuple. */
106 	pkgconf_tuple_define_global(client, "myvar=myvalue");
107 
108 	const char *found = pkgconf_tuple_find_global(client, "myvar");
109 	TEST_ASSERT_NONNULL(found);
110 	TEST_ASSERT_STRCMP_EQ(found, "myvalue");
111 
112 	pkgconf_tuple_free_global(client);
113 	pkgconf_client_free(client);
114 }
115 
116 static void
test_tuple_define_global_with_equals_in_value(void)117 test_tuple_define_global_with_equals_in_value(void)
118 {
119 	pkgconf_client_t *client = test_client_new();
120 
121 	// Only the first '=' should split key from value; subsequent '=' characters belong to the value.
122 	pkgconf_tuple_define_global(client, "CFLAGS=-DFOO=bar");
123 
124 	const char *found = pkgconf_tuple_find_global(client, "CFLAGS");
125 	TEST_ASSERT_NONNULL(found);
126 	TEST_ASSERT_STRCMP_EQ(found, "-DFOO=bar");
127 
128 	pkgconf_tuple_free_global(client);
129 	pkgconf_client_free(client);
130 }
131 
132 static void
test_tuple_global_multiple(void)133 test_tuple_global_multiple(void)
134 {
135 	pkgconf_client_t *client = test_client_new();
136 
137 	pkgconf_tuple_define_global(client, "a=1");
138 	pkgconf_tuple_define_global(client, "b=2");
139 	pkgconf_tuple_define_global(client, "c=3");
140 
141 	TEST_ASSERT_STRCMP_EQ(pkgconf_tuple_find_global(client, "a"), "1");
142 	TEST_ASSERT_STRCMP_EQ(pkgconf_tuple_find_global(client, "b"), "2");
143 	TEST_ASSERT_STRCMP_EQ(pkgconf_tuple_find_global(client, "c"), "3");
144 
145 	pkgconf_tuple_free_global(client);
146 	pkgconf_client_free(client);
147 }
148 
149 static void
test_tuple_global_free_resets(void)150 test_tuple_global_free_resets(void)
151 {
152 	pkgconf_client_t *client = test_client_new();
153 
154 	pkgconf_tuple_define_global(client, "temp=value");
155 	TEST_ASSERT_NONNULL(pkgconf_tuple_find_global(client, "temp"));
156 
157 	pkgconf_tuple_free_global(client);
158 	TEST_ASSERT_EMPTY_STRING(pkgconf_tuple_find_global(client, "temp"));
159 
160 	pkgconf_client_free(client);
161 }
162 
163 static void
test_tuple_free_empty(void)164 test_tuple_free_empty(void)
165 {
166 	pkgconf_list_t tuples = PKGCONF_LIST_INITIALIZER;
167 
168 	// Freeing an empty list should be a no-op. Mostly an ASan/leak check smoke test.
169 	pkgconf_tuple_free(&tuples);
170 }
171 
172 static void
test_tuple_define_variable_end_to_end(void)173 test_tuple_define_variable_end_to_end(void)
174 {
175 	pkgconf_client_t *client = test_client_new();
176 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
177 	bool saw_sysroot = false;
178 
179 	pkgconf_tuple_define_global(client, "myprefix=/opt/custom");
180 
181 	char *out = pkgconf_bytecode_eval_str(client, &vars, "-I${myprefix}/include", &saw_sysroot);
182 
183 	TEST_ASSERT_NONNULL(out);
184 	TEST_ASSERT_STRCMP_EQ(out, "-I/opt/custom/include");
185 
186 	free(out);
187 	pkgconf_variable_list_free(&vars);
188 	pkgconf_tuple_free_global(client);
189 	pkgconf_client_free(client);
190 }
191 
192 static void
test_tuple_define_variable_overrides_local(void)193 test_tuple_define_variable_overrides_local(void)
194 {
195 	pkgconf_client_t *client = test_client_new();
196 	pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
197 	bool saw_sysroot = false;
198 
199 	pkgconf_variable_t *v = pkgconf_variable_get_or_create(&vars, "prefix");
200 	TEST_ASSERT_NONNULL(v);
201 	pkgconf_buffer_reset(&v->bcbuf);
202 	pkgconf_bytecode_compile(&v->bcbuf, "/usr");
203 	pkgconf_bytecode_from_buffer(&v->bc, &v->bcbuf);
204 
205 	// Simulate user passing --define-variable=prefix=/custom.
206 	pkgconf_tuple_define_global(client, "prefix=/custom");
207 
208 	char *out = pkgconf_bytecode_eval_str(client, &vars, "${prefix}/lib", &saw_sysroot);
209 
210 	TEST_ASSERT_NONNULL(out);
211 	TEST_ASSERT_STRCMP_EQ(out, "/custom/lib");
212 
213 	free(out);
214 	pkgconf_variable_list_free(&vars);
215 	pkgconf_tuple_free_global(client);
216 	pkgconf_client_free(client);
217 }
218 
219 static void
test_tuple_escaped_quote(void)220 test_tuple_escaped_quote(void)
221 {
222 	pkgconf_client_t *client = test_client_new();
223 	pkgconf_list_t tuples = PKGCONF_LIST_INITIALIZER;
224 
225 	// A double-quoted value containing an escaped double-quote
226 	pkgconf_tuple_t *t = pkgconf_tuple_add(client, &tuples, "key", "\"a\\\"b\"", true, 0);
227 	TEST_ASSERT_NONNULL(t);
228 
229 	const char *found = pkgconf_tuple_find(client, &tuples, "key");
230 	TEST_ASSERT_NONNULL(found);
231 	TEST_ASSERT_STRCMP_EQ(found, "a\"b");
232 
233 	pkgconf_tuple_free(&tuples);
234 	pkgconf_client_free(client);
235 }
236 
237 int
main(int argc,char * argv[])238 main(int argc, char *argv[])
239 {
240 	(void) argc;
241 	const char *basename = pkgconf_path_find_basename(argv[0]);
242 
243 	TEST_RUN(basename, test_tuple_add_and_find);
244 	TEST_RUN(basename, test_tuple_find_absent);
245 	TEST_RUN(basename, test_tuple_add_multiple);
246 	TEST_RUN(basename, test_tuple_free_empty);
247 	TEST_RUN(basename, test_tuple_global_add_and_find);
248 	TEST_RUN(basename, test_tuple_global_find_absent);
249 	TEST_RUN(basename, test_tuple_define_global_kv_form);
250 	TEST_RUN(basename, test_tuple_define_global_with_equals_in_value);
251 	TEST_RUN(basename, test_tuple_global_multiple);
252 	TEST_RUN(basename, test_tuple_global_free_resets);
253 	TEST_RUN(basename, test_tuple_define_variable_end_to_end);
254 	TEST_RUN(basename, test_tuple_define_variable_overrides_local);
255 	TEST_RUN(basename, test_tuple_escaped_quote);
256 
257 	return EXIT_SUCCESS;
258 }
259