1*592efe25SPierre Pronchery /*
2*592efe25SPierre Pronchery * test-bytecode.c
3*592efe25SPierre Pronchery * Tests for the public libpkgconf bytecode API:
4*592efe25SPierre Pronchery * pkgconf_bytecode_compile and pkgconf_bytecode_eval_str.
5*592efe25SPierre Pronchery *
6*592efe25SPierre Pronchery * SPDX-License-Identifier: pkgconf
7*592efe25SPierre Pronchery *
8*592efe25SPierre Pronchery * Copyright (c) 2025 pkgconf authors (see AUTHORS).
9*592efe25SPierre Pronchery *
10*592efe25SPierre Pronchery * Permission to use, copy, modify, and/or distribute this software for any
11*592efe25SPierre Pronchery * purpose with or without fee is hereby granted, provided that the above
12*592efe25SPierre Pronchery * copyright notice and this permission notice appear in all copies.
13*592efe25SPierre Pronchery *
14*592efe25SPierre Pronchery * This software is provided 'as is' and without any warranty, express or
15*592efe25SPierre Pronchery * implied. In no event shall the authors be liable for any damages arising
16*592efe25SPierre Pronchery * from the use of this software.
17*592efe25SPierre Pronchery */
18*592efe25SPierre Pronchery
19*592efe25SPierre Pronchery #include <libpkgconf/stdinc.h>
20*592efe25SPierre Pronchery #include <libpkgconf/libpkgconf.h>
21*592efe25SPierre Pronchery #include "test-api.h"
22*592efe25SPierre Pronchery
23*592efe25SPierre Pronchery /*
24*592efe25SPierre Pronchery * Build a variable list with the given key/value pairs. Caller frees
25*592efe25SPierre Pronchery * with pkgconf_variable_list_free().
26*592efe25SPierre Pronchery *
27*592efe25SPierre Pronchery * The key/value pairs are stored by compiling `value` as bytecode and
28*592efe25SPierre Pronchery * stashing the result on a pkgconf_variable_t inside the list, which
29*592efe25SPierre Pronchery * is how the parser builds variable scopes for real .pc files.
30*592efe25SPierre Pronchery */
31*592efe25SPierre Pronchery static void
seed_variable(pkgconf_list_t * vars,const char * key,const char * value)32*592efe25SPierre Pronchery seed_variable(pkgconf_list_t *vars, const char *key, const char *value)
33*592efe25SPierre Pronchery {
34*592efe25SPierre Pronchery pkgconf_variable_t *v = pkgconf_variable_get_or_create(vars, key);
35*592efe25SPierre Pronchery TEST_ASSERT_NONNULL(v);
36*592efe25SPierre Pronchery
37*592efe25SPierre Pronchery pkgconf_buffer_reset(&v->bcbuf);
38*592efe25SPierre Pronchery pkgconf_bytecode_compile(&v->bcbuf, value);
39*592efe25SPierre Pronchery pkgconf_bytecode_from_buffer(&v->bc, &v->bcbuf);
40*592efe25SPierre Pronchery }
41*592efe25SPierre Pronchery
42*592efe25SPierre Pronchery static void
test_emit_text_and_eval(void)43*592efe25SPierre Pronchery test_emit_text_and_eval(void)
44*592efe25SPierre Pronchery {
45*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
46*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
47*592efe25SPierre Pronchery pkgconf_buffer_t bcbuf = PKGCONF_BUFFER_INITIALIZER;
48*592efe25SPierre Pronchery pkgconf_bytecode_t bc;
49*592efe25SPierre Pronchery bool saw_sysroot = false;
50*592efe25SPierre Pronchery
51*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_bytecode_emit_text(&bcbuf, "plain", 5));
52*592efe25SPierre Pronchery pkgconf_bytecode_from_buffer(&bc, &bcbuf);
53*592efe25SPierre Pronchery
54*592efe25SPierre Pronchery pkgconf_buffer_t out = PKGCONF_BUFFER_INITIALIZER;
55*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_bytecode_eval(client, &vars, &bc, &out, &saw_sysroot));
56*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(pkgconf_buffer_str_or_empty(&out), "plain");
57*592efe25SPierre Pronchery
58*592efe25SPierre Pronchery pkgconf_buffer_finalize(&out);
59*592efe25SPierre Pronchery pkgconf_buffer_finalize(&bcbuf);
60*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
61*592efe25SPierre Pronchery pkgconf_client_free(client);
62*592efe25SPierre Pronchery }
63*592efe25SPierre Pronchery
64*592efe25SPierre Pronchery static void
test_emit_guards(void)65*592efe25SPierre Pronchery test_emit_guards(void)
66*592efe25SPierre Pronchery {
67*592efe25SPierre Pronchery pkgconf_buffer_t bcbuf = PKGCONF_BUFFER_INITIALIZER;
68*592efe25SPierre Pronchery
69*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_bytecode_emit_text(&bcbuf, NULL, 5));
70*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_bytecode_emit_text(&bcbuf, "x", 0));
71*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_bytecode_emit_var(&bcbuf, NULL, 3));
72*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_bytecode_emit_var(&bcbuf, "x", 0));
73*592efe25SPierre Pronchery
74*592efe25SPierre Pronchery TEST_ASSERT_EQ(pkgconf_buffer_len(&bcbuf), 0);
75*592efe25SPierre Pronchery
76*592efe25SPierre Pronchery pkgconf_buffer_finalize(&bcbuf);
77*592efe25SPierre Pronchery }
78*592efe25SPierre Pronchery
79*592efe25SPierre Pronchery static void
test_emit_var_and_eval(void)80*592efe25SPierre Pronchery test_emit_var_and_eval(void)
81*592efe25SPierre Pronchery {
82*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
83*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
84*592efe25SPierre Pronchery pkgconf_buffer_t bcbuf = PKGCONF_BUFFER_INITIALIZER;
85*592efe25SPierre Pronchery pkgconf_bytecode_t bc;
86*592efe25SPierre Pronchery bool saw_sysroot = false;
87*592efe25SPierre Pronchery
88*592efe25SPierre Pronchery seed_variable(&vars, "name", "world");
89*592efe25SPierre Pronchery
90*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_bytecode_emit_text(&bcbuf, "hello ", 6));
91*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_bytecode_emit_var(&bcbuf, "name", 4));
92*592efe25SPierre Pronchery pkgconf_bytecode_from_buffer(&bc, &bcbuf);
93*592efe25SPierre Pronchery
94*592efe25SPierre Pronchery pkgconf_buffer_t out = PKGCONF_BUFFER_INITIALIZER;
95*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_bytecode_eval(client, &vars, &bc, &out, &saw_sysroot));
96*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(pkgconf_buffer_str_or_empty(&out), "hello world");
97*592efe25SPierre Pronchery
98*592efe25SPierre Pronchery pkgconf_buffer_finalize(&out);
99*592efe25SPierre Pronchery pkgconf_buffer_finalize(&bcbuf);
100*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
101*592efe25SPierre Pronchery pkgconf_client_free(client);
102*592efe25SPierre Pronchery }
103*592efe25SPierre Pronchery
104*592efe25SPierre Pronchery static void
test_emit_sysroot_and_eval(void)105*592efe25SPierre Pronchery test_emit_sysroot_and_eval(void)
106*592efe25SPierre Pronchery {
107*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
108*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
109*592efe25SPierre Pronchery pkgconf_buffer_t bcbuf = PKGCONF_BUFFER_INITIALIZER;
110*592efe25SPierre Pronchery pkgconf_bytecode_t bc;
111*592efe25SPierre Pronchery bool saw_sysroot = false;
112*592efe25SPierre Pronchery
113*592efe25SPierre Pronchery pkgconf_client_set_sysroot_dir(client, "/sysroot");
114*592efe25SPierre Pronchery
115*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_bytecode_emit_sysroot(&bcbuf));
116*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_bytecode_emit_text(&bcbuf, "/usr/include", 12));
117*592efe25SPierre Pronchery pkgconf_bytecode_from_buffer(&bc, &bcbuf);
118*592efe25SPierre Pronchery
119*592efe25SPierre Pronchery pkgconf_buffer_t out = PKGCONF_BUFFER_INITIALIZER;
120*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_bytecode_eval(client, &vars, &bc, &out, &saw_sysroot));
121*592efe25SPierre Pronchery TEST_ASSERT_TRUE(saw_sysroot);
122*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(pkgconf_buffer_str_or_empty(&out), "/sysroot/usr/include");
123*592efe25SPierre Pronchery
124*592efe25SPierre Pronchery pkgconf_buffer_finalize(&out);
125*592efe25SPierre Pronchery pkgconf_buffer_finalize(&bcbuf);
126*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
127*592efe25SPierre Pronchery pkgconf_client_free(client);
128*592efe25SPierre Pronchery }
129*592efe25SPierre Pronchery
130*592efe25SPierre Pronchery static void
test_eval_null_args(void)131*592efe25SPierre Pronchery test_eval_null_args(void)
132*592efe25SPierre Pronchery {
133*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
134*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
135*592efe25SPierre Pronchery pkgconf_buffer_t out = PKGCONF_BUFFER_INITIALIZER;
136*592efe25SPierre Pronchery pkgconf_bytecode_t bc = { NULL, 0 };
137*592efe25SPierre Pronchery
138*592efe25SPierre Pronchery // NULL client, bc, or out all return false.
139*592efe25SPierre Pronchery TEST_ASSERT_FALSE(pkgconf_bytecode_eval(NULL, &vars, &bc, &out, NULL));
140*592efe25SPierre Pronchery TEST_ASSERT_FALSE(pkgconf_bytecode_eval(client, &vars, NULL, &out, NULL));
141*592efe25SPierre Pronchery TEST_ASSERT_FALSE(pkgconf_bytecode_eval(client, &vars, &bc, NULL, NULL));
142*592efe25SPierre Pronchery
143*592efe25SPierre Pronchery pkgconf_buffer_finalize(&out);
144*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
145*592efe25SPierre Pronchery pkgconf_client_free(client);
146*592efe25SPierre Pronchery }
147*592efe25SPierre Pronchery
148*592efe25SPierre Pronchery static void
test_compile_null_args(void)149*592efe25SPierre Pronchery test_compile_null_args(void)
150*592efe25SPierre Pronchery {
151*592efe25SPierre Pronchery pkgconf_buffer_t out = PKGCONF_BUFFER_INITIALIZER;
152*592efe25SPierre Pronchery
153*592efe25SPierre Pronchery TEST_ASSERT_FALSE(pkgconf_bytecode_compile(NULL, "x"));
154*592efe25SPierre Pronchery TEST_ASSERT_FALSE(pkgconf_bytecode_compile(&out, NULL));
155*592efe25SPierre Pronchery
156*592efe25SPierre Pronchery pkgconf_buffer_finalize(&out);
157*592efe25SPierre Pronchery }
158*592efe25SPierre Pronchery
159*592efe25SPierre Pronchery static void
test_dollar_escape(void)160*592efe25SPierre Pronchery test_dollar_escape(void)
161*592efe25SPierre Pronchery {
162*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
163*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
164*592efe25SPierre Pronchery bool saw_sysroot = false;
165*592efe25SPierre Pronchery
166*592efe25SPierre Pronchery // $$ collapses to a literal $; the following text is NOT treated as a variable reference
167*592efe25SPierre Pronchery char *out = pkgconf_bytecode_eval_str(client, &vars, "price: $$5 and $${notavar}", &saw_sysroot);
168*592efe25SPierre Pronchery TEST_ASSERT_NONNULL(out);
169*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(out, "price: $5 and ${notavar}");
170*592efe25SPierre Pronchery
171*592efe25SPierre Pronchery free(out);
172*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
173*592efe25SPierre Pronchery pkgconf_client_free(client);
174*592efe25SPierre Pronchery }
175*592efe25SPierre Pronchery
176*592efe25SPierre Pronchery static void
test_malformed_unclosed(void)177*592efe25SPierre Pronchery test_malformed_unclosed(void)
178*592efe25SPierre Pronchery {
179*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
180*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
181*592efe25SPierre Pronchery bool saw_sysroot = false;
182*592efe25SPierre Pronchery
183*592efe25SPierre Pronchery // A ${ with no closing } is emitted as literal text
184*592efe25SPierre Pronchery char *out = pkgconf_bytecode_eval_str(client, &vars, "broken ${unclosed", &saw_sysroot);
185*592efe25SPierre Pronchery TEST_ASSERT_NONNULL(out);
186*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(out, "broken ${unclosed");
187*592efe25SPierre Pronchery
188*592efe25SPierre Pronchery free(out);
189*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
190*592efe25SPierre Pronchery pkgconf_client_free(client);
191*592efe25SPierre Pronchery }
192*592efe25SPierre Pronchery
193*592efe25SPierre Pronchery static void
test_empty_braces(void)194*592efe25SPierre Pronchery test_empty_braces(void)
195*592efe25SPierre Pronchery {
196*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
197*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
198*592efe25SPierre Pronchery bool saw_sysroot = false;
199*592efe25SPierre Pronchery
200*592efe25SPierre Pronchery // ${} has a zero-length name; it's emitted as literal text rather than treated as a variable
201*592efe25SPierre Pronchery char *out = pkgconf_bytecode_eval_str(client, &vars, "empty ${} here", &saw_sysroot);
202*592efe25SPierre Pronchery TEST_ASSERT_NONNULL(out);
203*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(out, "empty ${} here");
204*592efe25SPierre Pronchery
205*592efe25SPierre Pronchery free(out);
206*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
207*592efe25SPierre Pronchery pkgconf_client_free(client);
208*592efe25SPierre Pronchery }
209*592efe25SPierre Pronchery
210*592efe25SPierre Pronchery static void
test_compile_time_sysroot(void)211*592efe25SPierre Pronchery test_compile_time_sysroot(void)
212*592efe25SPierre Pronchery {
213*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
214*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
215*592efe25SPierre Pronchery bool saw_sysroot = false;
216*592efe25SPierre Pronchery
217*592efe25SPierre Pronchery pkgconf_client_set_sysroot_dir(client, "/sysroot");
218*592efe25SPierre Pronchery
219*592efe25SPierre Pronchery // ${pc_sysrootdir} is special-cased at compile time into an OP_SYSROOT op rather than a variable lookup
220*592efe25SPierre Pronchery char *out = pkgconf_bytecode_eval_str(client, &vars, "${pc_sysrootdir}/usr/lib", &saw_sysroot);
221*592efe25SPierre Pronchery TEST_ASSERT_NONNULL(out);
222*592efe25SPierre Pronchery TEST_ASSERT_TRUE(saw_sysroot);
223*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(out, "/sysroot/usr/lib");
224*592efe25SPierre Pronchery
225*592efe25SPierre Pronchery free(out);
226*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
227*592efe25SPierre Pronchery pkgconf_client_free(client);
228*592efe25SPierre Pronchery }
229*592efe25SPierre Pronchery
230*592efe25SPierre Pronchery static void
test_sysroot_dot_disables(void)231*592efe25SPierre Pronchery test_sysroot_dot_disables(void)
232*592efe25SPierre Pronchery {
233*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
234*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
235*592efe25SPierre Pronchery bool saw_sysroot = false;
236*592efe25SPierre Pronchery
237*592efe25SPierre Pronchery // A sysroot of "." disables sysroot rewriting: ${pc_sysrootdir} expands to empty
238*592efe25SPierre Pronchery pkgconf_client_set_sysroot_dir(client, ".");
239*592efe25SPierre Pronchery
240*592efe25SPierre Pronchery char *out = pkgconf_bytecode_eval_str(client, &vars, "${pc_sysrootdir}/usr/lib", &saw_sysroot);
241*592efe25SPierre Pronchery TEST_ASSERT_NONNULL(out);
242*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(out, "/usr/lib");
243*592efe25SPierre Pronchery
244*592efe25SPierre Pronchery free(out);
245*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
246*592efe25SPierre Pronchery pkgconf_client_free(client);
247*592efe25SPierre Pronchery }
248*592efe25SPierre Pronchery
249*592efe25SPierre Pronchery static void
test_sysroot_root_disables(void)250*592efe25SPierre Pronchery test_sysroot_root_disables(void)
251*592efe25SPierre Pronchery {
252*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
253*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
254*592efe25SPierre Pronchery bool saw_sysroot = false;
255*592efe25SPierre Pronchery
256*592efe25SPierre Pronchery // A sysroot of "/" (the root dir) disables rewriting.
257*592efe25SPierre Pronchery pkgconf_client_set_sysroot_dir(client, "/");
258*592efe25SPierre Pronchery
259*592efe25SPierre Pronchery char *out = pkgconf_bytecode_eval_str(client, &vars, "${pc_sysrootdir}/usr/lib", &saw_sysroot);
260*592efe25SPierre Pronchery TEST_ASSERT_NONNULL(out);
261*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(out, "/usr/lib");
262*592efe25SPierre Pronchery
263*592efe25SPierre Pronchery free(out);
264*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
265*592efe25SPierre Pronchery pkgconf_client_free(client);
266*592efe25SPierre Pronchery }
267*592efe25SPierre Pronchery
268*592efe25SPierre Pronchery static void
test_sysroot_trailing_slash_trimmed(void)269*592efe25SPierre Pronchery test_sysroot_trailing_slash_trimmed(void)
270*592efe25SPierre Pronchery {
271*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
272*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
273*592efe25SPierre Pronchery bool saw_sysroot = false;
274*592efe25SPierre Pronchery
275*592efe25SPierre Pronchery // Trailing slashes on the sysroot are normalized away, so the result doesn't get a doubled slash
276*592efe25SPierre Pronchery pkgconf_client_set_sysroot_dir(client, "/sysroot///");
277*592efe25SPierre Pronchery
278*592efe25SPierre Pronchery char *out = pkgconf_bytecode_eval_str(client, &vars, "${pc_sysrootdir}/usr/lib", &saw_sysroot);
279*592efe25SPierre Pronchery TEST_ASSERT_NONNULL(out);
280*592efe25SPierre Pronchery TEST_ASSERT_TRUE(saw_sysroot);
281*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(out, "/sysroot/usr/lib");
282*592efe25SPierre Pronchery
283*592efe25SPierre Pronchery free(out);
284*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
285*592efe25SPierre Pronchery pkgconf_client_free(client);
286*592efe25SPierre Pronchery }
287*592efe25SPierre Pronchery
288*592efe25SPierre Pronchery static void
test_sysroot_normalizes_to_root_disables(void)289*592efe25SPierre Pronchery test_sysroot_normalizes_to_root_disables(void)
290*592efe25SPierre Pronchery {
291*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
292*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
293*592efe25SPierre Pronchery bool saw_sysroot = false;
294*592efe25SPierre Pronchery
295*592efe25SPierre Pronchery /* A sysroot of "//" survives the bare-"/" early check but normalizes down to "/" after
296*592efe25SPierre Pronchery * trailing-slash trimming, which then disables rewriting (empty sysroot) */
297*592efe25SPierre Pronchery pkgconf_client_set_sysroot_dir(client, "//");
298*592efe25SPierre Pronchery
299*592efe25SPierre Pronchery char *out = pkgconf_bytecode_eval_str(client, &vars, "${pc_sysrootdir}/usr/lib", &saw_sysroot);
300*592efe25SPierre Pronchery TEST_ASSERT_NONNULL(out);
301*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(out, "/usr/lib");
302*592efe25SPierre Pronchery
303*592efe25SPierre Pronchery free(out);
304*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
305*592efe25SPierre Pronchery pkgconf_client_free(client);
306*592efe25SPierre Pronchery }
307*592efe25SPierre Pronchery
308*592efe25SPierre Pronchery static void
test_circular_reference(void)309*592efe25SPierre Pronchery test_circular_reference(void)
310*592efe25SPierre Pronchery {
311*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
312*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
313*592efe25SPierre Pronchery bool saw_sysroot = false;
314*592efe25SPierre Pronchery
315*592efe25SPierre Pronchery // A variable that references itself must not infinite-loop: the `expanding` guard breaks the cycle
316*592efe25SPierre Pronchery seed_variable(&vars, "loop", "${loop}");
317*592efe25SPierre Pronchery
318*592efe25SPierre Pronchery char *out = pkgconf_bytecode_eval_str(client, &vars, "${loop}", &saw_sysroot);
319*592efe25SPierre Pronchery
320*592efe25SPierre Pronchery /* The self-reference is broken; behavior is "expands to nothing further"
321*592efe25SPierre Pronchery * We mostly care that it returns rather than hanging or crashing */
322*592efe25SPierre Pronchery if (out != NULL)
323*592efe25SPierre Pronchery free(out);
324*592efe25SPierre Pronchery
325*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
326*592efe25SPierre Pronchery pkgconf_client_free(client);
327*592efe25SPierre Pronchery }
328*592efe25SPierre Pronchery
329*592efe25SPierre Pronchery static void
test_references_var(void)330*592efe25SPierre Pronchery test_references_var(void)
331*592efe25SPierre Pronchery {
332*592efe25SPierre Pronchery pkgconf_buffer_t bcbuf = PKGCONF_BUFFER_INITIALIZER;
333*592efe25SPierre Pronchery
334*592efe25SPierre Pronchery pkgconf_bytecode_compile(&bcbuf, "-I${includedir} -L${libdir}");
335*592efe25SPierre Pronchery
336*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_bytecode_references_var(&bcbuf, "includedir"));
337*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_bytecode_references_var(&bcbuf, "libdir"));
338*592efe25SPierre Pronchery TEST_ASSERT_FALSE(pkgconf_bytecode_references_var(&bcbuf, "prefix"));
339*592efe25SPierre Pronchery
340*592efe25SPierre Pronchery TEST_ASSERT_FALSE(pkgconf_bytecode_references_var(NULL, "x"));
341*592efe25SPierre Pronchery TEST_ASSERT_FALSE(pkgconf_bytecode_references_var(&bcbuf, NULL));
342*592efe25SPierre Pronchery
343*592efe25SPierre Pronchery pkgconf_buffer_finalize(&bcbuf);
344*592efe25SPierre Pronchery }
345*592efe25SPierre Pronchery
346*592efe25SPierre Pronchery static void
test_references_var_text_only(void)347*592efe25SPierre Pronchery test_references_var_text_only(void)
348*592efe25SPierre Pronchery {
349*592efe25SPierre Pronchery pkgconf_buffer_t bcbuf = PKGCONF_BUFFER_INITIALIZER;
350*592efe25SPierre Pronchery
351*592efe25SPierre Pronchery pkgconf_bytecode_compile(&bcbuf, "just plain text");
352*592efe25SPierre Pronchery
353*592efe25SPierre Pronchery TEST_ASSERT_FALSE(pkgconf_bytecode_references_var(&bcbuf, "anything"));
354*592efe25SPierre Pronchery
355*592efe25SPierre Pronchery pkgconf_buffer_finalize(&bcbuf);
356*592efe25SPierre Pronchery }
357*592efe25SPierre Pronchery
358*592efe25SPierre Pronchery static void
test_rewrite_selfrefs(void)359*592efe25SPierre Pronchery test_rewrite_selfrefs(void)
360*592efe25SPierre Pronchery {
361*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
362*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
363*592efe25SPierre Pronchery bool saw_sysroot = false;
364*592efe25SPierre Pronchery
365*592efe25SPierre Pronchery pkgconf_buffer_t prev = PKGCONF_BUFFER_INITIALIZER;
366*592efe25SPierre Pronchery pkgconf_bytecode_compile(&prev, "old");
367*592efe25SPierre Pronchery
368*592efe25SPierre Pronchery pkgconf_buffer_t rhs = PKGCONF_BUFFER_INITIALIZER;
369*592efe25SPierre Pronchery pkgconf_bytecode_compile(&rhs, "${foo} new");
370*592efe25SPierre Pronchery
371*592efe25SPierre Pronchery pkgconf_buffer_t rewritten = PKGCONF_BUFFER_INITIALIZER;
372*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_bytecode_rewrite_selfrefs(&rewritten, &rhs, "foo", &prev));
373*592efe25SPierre Pronchery
374*592efe25SPierre Pronchery pkgconf_bytecode_t bc;
375*592efe25SPierre Pronchery pkgconf_bytecode_from_buffer(&bc, &rewritten);
376*592efe25SPierre Pronchery
377*592efe25SPierre Pronchery pkgconf_buffer_t out = PKGCONF_BUFFER_INITIALIZER;
378*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_bytecode_eval(client, &vars, &bc, &out, &saw_sysroot));
379*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(pkgconf_buffer_str_or_empty(&out), "old new");
380*592efe25SPierre Pronchery
381*592efe25SPierre Pronchery pkgconf_buffer_finalize(&out);
382*592efe25SPierre Pronchery pkgconf_buffer_finalize(&rewritten);
383*592efe25SPierre Pronchery pkgconf_buffer_finalize(&rhs);
384*592efe25SPierre Pronchery pkgconf_buffer_finalize(&prev);
385*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
386*592efe25SPierre Pronchery pkgconf_client_free(client);
387*592efe25SPierre Pronchery }
388*592efe25SPierre Pronchery
389*592efe25SPierre Pronchery static void
test_rewrite_selfrefs_no_match(void)390*592efe25SPierre Pronchery test_rewrite_selfrefs_no_match(void)
391*592efe25SPierre Pronchery {
392*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
393*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
394*592efe25SPierre Pronchery bool saw_sysroot = false;
395*592efe25SPierre Pronchery
396*592efe25SPierre Pronchery pkgconf_buffer_t prev = PKGCONF_BUFFER_INITIALIZER;
397*592efe25SPierre Pronchery pkgconf_bytecode_compile(&prev, "PREV");
398*592efe25SPierre Pronchery
399*592efe25SPierre Pronchery pkgconf_buffer_t rhs = PKGCONF_BUFFER_INITIALIZER;
400*592efe25SPierre Pronchery pkgconf_bytecode_compile(&rhs, "${other} tail");
401*592efe25SPierre Pronchery
402*592efe25SPierre Pronchery pkgconf_buffer_t rewritten = PKGCONF_BUFFER_INITIALIZER;
403*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_bytecode_rewrite_selfrefs(&rewritten, &rhs, "foo", &prev));
404*592efe25SPierre Pronchery
405*592efe25SPierre Pronchery // ${other} survives; seed it so eval can resolve it
406*592efe25SPierre Pronchery seed_variable(&vars, "other", "OTHER");
407*592efe25SPierre Pronchery
408*592efe25SPierre Pronchery pkgconf_bytecode_t bc;
409*592efe25SPierre Pronchery pkgconf_bytecode_from_buffer(&bc, &rewritten);
410*592efe25SPierre Pronchery
411*592efe25SPierre Pronchery pkgconf_buffer_t out = PKGCONF_BUFFER_INITIALIZER;
412*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_bytecode_eval(client, &vars, &bc, &out, &saw_sysroot));
413*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(pkgconf_buffer_str_or_empty(&out), "OTHER tail");
414*592efe25SPierre Pronchery
415*592efe25SPierre Pronchery pkgconf_buffer_finalize(&out);
416*592efe25SPierre Pronchery pkgconf_buffer_finalize(&rewritten);
417*592efe25SPierre Pronchery pkgconf_buffer_finalize(&rhs);
418*592efe25SPierre Pronchery pkgconf_buffer_finalize(&prev);
419*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
420*592efe25SPierre Pronchery pkgconf_client_free(client);
421*592efe25SPierre Pronchery }
422*592efe25SPierre Pronchery
423*592efe25SPierre Pronchery static void
test_eval_plain_text(void)424*592efe25SPierre Pronchery test_eval_plain_text(void)
425*592efe25SPierre Pronchery {
426*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
427*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
428*592efe25SPierre Pronchery bool saw_sysroot = false;
429*592efe25SPierre Pronchery
430*592efe25SPierre Pronchery // A string with no variable references should round-trip unchanged.
431*592efe25SPierre Pronchery char *out = pkgconf_bytecode_eval_str(client, &vars, "plain text value", &saw_sysroot);
432*592efe25SPierre Pronchery
433*592efe25SPierre Pronchery TEST_ASSERT_NONNULL(out);
434*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(out, "plain text value");
435*592efe25SPierre Pronchery TEST_ASSERT_FALSE(saw_sysroot);
436*592efe25SPierre Pronchery
437*592efe25SPierre Pronchery free(out);
438*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
439*592efe25SPierre Pronchery pkgconf_client_free(client);
440*592efe25SPierre Pronchery }
441*592efe25SPierre Pronchery
442*592efe25SPierre Pronchery static void
test_eval_variable_substitution(void)443*592efe25SPierre Pronchery test_eval_variable_substitution(void)
444*592efe25SPierre Pronchery {
445*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
446*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
447*592efe25SPierre Pronchery bool saw_sysroot = false;
448*592efe25SPierre Pronchery
449*592efe25SPierre Pronchery seed_variable(&vars, "prefix", "/opt/foo");
450*592efe25SPierre Pronchery
451*592efe25SPierre Pronchery char *out = pkgconf_bytecode_eval_str(client, &vars, "${prefix}/lib", &saw_sysroot);
452*592efe25SPierre Pronchery
453*592efe25SPierre Pronchery TEST_ASSERT_NONNULL(out);
454*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(out, "/opt/foo/lib");
455*592efe25SPierre Pronchery TEST_ASSERT_FALSE(saw_sysroot);
456*592efe25SPierre Pronchery
457*592efe25SPierre Pronchery free(out);
458*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
459*592efe25SPierre Pronchery pkgconf_client_free(client);
460*592efe25SPierre Pronchery }
461*592efe25SPierre Pronchery
462*592efe25SPierre Pronchery static void
test_eval_nested_variables(void)463*592efe25SPierre Pronchery test_eval_nested_variables(void)
464*592efe25SPierre Pronchery {
465*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
466*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
467*592efe25SPierre Pronchery bool saw_sysroot = false;
468*592efe25SPierre Pronchery
469*592efe25SPierre Pronchery // Recursive expansion: libdir references prefix.
470*592efe25SPierre Pronchery seed_variable(&vars, "prefix", "/usr/local");
471*592efe25SPierre Pronchery seed_variable(&vars, "libdir", "${prefix}/lib");
472*592efe25SPierre Pronchery
473*592efe25SPierre Pronchery char *out = pkgconf_bytecode_eval_str(client, &vars, "-L${libdir}", &saw_sysroot);
474*592efe25SPierre Pronchery
475*592efe25SPierre Pronchery TEST_ASSERT_NONNULL(out);
476*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(out, "-L/usr/local/lib");
477*592efe25SPierre Pronchery
478*592efe25SPierre Pronchery free(out);
479*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
480*592efe25SPierre Pronchery pkgconf_client_free(client);
481*592efe25SPierre Pronchery }
482*592efe25SPierre Pronchery
483*592efe25SPierre Pronchery static void
test_eval_undefined_variable(void)484*592efe25SPierre Pronchery test_eval_undefined_variable(void)
485*592efe25SPierre Pronchery {
486*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
487*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
488*592efe25SPierre Pronchery bool saw_sysroot = false;
489*592efe25SPierre Pronchery
490*592efe25SPierre Pronchery // Referencing an undefined variable should produce empty substitution, not failure
491*592efe25SPierre Pronchery char *out = pkgconf_bytecode_eval_str(client, &vars, "prefix=${nonexistent}/end", &saw_sysroot);
492*592efe25SPierre Pronchery
493*592efe25SPierre Pronchery TEST_ASSERT_NONNULL(out);
494*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(out, "prefix=/end");
495*592efe25SPierre Pronchery
496*592efe25SPierre Pronchery free(out);
497*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
498*592efe25SPierre Pronchery pkgconf_client_free(client);
499*592efe25SPierre Pronchery }
500*592efe25SPierre Pronchery
501*592efe25SPierre Pronchery static void
test_eval_multiple_variables(void)502*592efe25SPierre Pronchery test_eval_multiple_variables(void)
503*592efe25SPierre Pronchery {
504*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
505*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
506*592efe25SPierre Pronchery bool saw_sysroot = false;
507*592efe25SPierre Pronchery
508*592efe25SPierre Pronchery seed_variable(&vars, "prefix", "/usr");
509*592efe25SPierre Pronchery seed_variable(&vars, "exec_prefix", "/usr/local");
510*592efe25SPierre Pronchery
511*592efe25SPierre Pronchery char *out = pkgconf_bytecode_eval_str(client, &vars, "-I${prefix}/include -L${exec_prefix}/lib", &saw_sysroot);
512*592efe25SPierre Pronchery
513*592efe25SPierre Pronchery TEST_ASSERT_NONNULL(out);
514*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(out, "-I/usr/include -L/usr/local/lib");
515*592efe25SPierre Pronchery
516*592efe25SPierre Pronchery free(out);
517*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
518*592efe25SPierre Pronchery pkgconf_client_free(client);
519*592efe25SPierre Pronchery }
520*592efe25SPierre Pronchery
521*592efe25SPierre Pronchery static void
test_eval_empty_input(void)522*592efe25SPierre Pronchery test_eval_empty_input(void)
523*592efe25SPierre Pronchery {
524*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
525*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
526*592efe25SPierre Pronchery bool saw_sysroot = false;
527*592efe25SPierre Pronchery
528*592efe25SPierre Pronchery char *out = pkgconf_bytecode_eval_str(client, &vars, "", &saw_sysroot);
529*592efe25SPierre Pronchery
530*592efe25SPierre Pronchery // An empty input may evaluate to either NULL or an empty string
531*592efe25SPierre Pronchery if (out != NULL)
532*592efe25SPierre Pronchery {
533*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(out, "");
534*592efe25SPierre Pronchery free(out);
535*592efe25SPierre Pronchery }
536*592efe25SPierre Pronchery
537*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
538*592efe25SPierre Pronchery pkgconf_client_free(client);
539*592efe25SPierre Pronchery }
540*592efe25SPierre Pronchery
541*592efe25SPierre Pronchery static void
test_eval_sysroot_detection(void)542*592efe25SPierre Pronchery test_eval_sysroot_detection(void)
543*592efe25SPierre Pronchery {
544*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
545*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
546*592efe25SPierre Pronchery bool saw_sysroot = false;
547*592efe25SPierre Pronchery
548*592efe25SPierre Pronchery pkgconf_client_set_sysroot_dir(client, "/sysroot");
549*592efe25SPierre Pronchery
550*592efe25SPierre Pronchery seed_variable(&vars, "pc_sysrootdir", "/sysroot");
551*592efe25SPierre Pronchery seed_variable(&vars, "includedir", "${pc_sysrootdir}/usr/include");
552*592efe25SPierre Pronchery
553*592efe25SPierre Pronchery char *out = pkgconf_bytecode_eval_str(client, &vars, "${includedir}", &saw_sysroot);
554*592efe25SPierre Pronchery
555*592efe25SPierre Pronchery TEST_ASSERT_NONNULL(out);
556*592efe25SPierre Pronchery TEST_ASSERT_TRUE(saw_sysroot);
557*592efe25SPierre Pronchery
558*592efe25SPierre Pronchery free(out);
559*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
560*592efe25SPierre Pronchery pkgconf_client_free(client);
561*592efe25SPierre Pronchery }
562*592efe25SPierre Pronchery
563*592efe25SPierre Pronchery static void
test_compile_produces_nonempty_buffer(void)564*592efe25SPierre Pronchery test_compile_produces_nonempty_buffer(void)
565*592efe25SPierre Pronchery {
566*592efe25SPierre Pronchery pkgconf_buffer_t bc = PKGCONF_BUFFER_INITIALIZER;
567*592efe25SPierre Pronchery
568*592efe25SPierre Pronchery pkgconf_bytecode_compile(&bc, "hello ${world}");
569*592efe25SPierre Pronchery
570*592efe25SPierre Pronchery // The compiled bytecode buffer should be non-empty
571*592efe25SPierre Pronchery TEST_ASSERT_NE(pkgconf_buffer_len(&bc), 0);
572*592efe25SPierre Pronchery
573*592efe25SPierre Pronchery pkgconf_buffer_finalize(&bc);
574*592efe25SPierre Pronchery }
575*592efe25SPierre Pronchery
576*592efe25SPierre Pronchery static void
test_compile_eval_roundtrip(void)577*592efe25SPierre Pronchery test_compile_eval_roundtrip(void)
578*592efe25SPierre Pronchery {
579*592efe25SPierre Pronchery pkgconf_client_t *client = test_client_new();
580*592efe25SPierre Pronchery pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER;
581*592efe25SPierre Pronchery bool saw_sysroot = false;
582*592efe25SPierre Pronchery
583*592efe25SPierre Pronchery // Compile directly, then evaluate via the bc field on a variable
584*592efe25SPierre Pronchery seed_variable(&vars, "name", "world");
585*592efe25SPierre Pronchery
586*592efe25SPierre Pronchery pkgconf_variable_t *v = pkgconf_variable_get_or_create(&vars, "greeting");
587*592efe25SPierre Pronchery TEST_ASSERT_NONNULL(v);
588*592efe25SPierre Pronchery pkgconf_buffer_reset(&v->bcbuf);
589*592efe25SPierre Pronchery pkgconf_bytecode_compile(&v->bcbuf, "hello ${name}");
590*592efe25SPierre Pronchery pkgconf_bytecode_from_buffer(&v->bc, &v->bcbuf);
591*592efe25SPierre Pronchery
592*592efe25SPierre Pronchery char *out = pkgconf_variable_eval_str(client, &vars, v, &saw_sysroot);
593*592efe25SPierre Pronchery TEST_ASSERT_NONNULL(out);
594*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(out, "hello world");
595*592efe25SPierre Pronchery
596*592efe25SPierre Pronchery free(out);
597*592efe25SPierre Pronchery pkgconf_variable_list_free(&vars);
598*592efe25SPierre Pronchery pkgconf_client_free(client);
599*592efe25SPierre Pronchery }
600*592efe25SPierre Pronchery
601*592efe25SPierre Pronchery int
main(int argc,char * argv[])602*592efe25SPierre Pronchery main(int argc, char *argv[])
603*592efe25SPierre Pronchery {
604*592efe25SPierre Pronchery (void) argc;
605*592efe25SPierre Pronchery const char *basename = pkgconf_path_find_basename(argv[0]);
606*592efe25SPierre Pronchery
607*592efe25SPierre Pronchery TEST_RUN(basename, test_eval_plain_text);
608*592efe25SPierre Pronchery TEST_RUN(basename, test_eval_empty_input);
609*592efe25SPierre Pronchery TEST_RUN(basename, test_eval_variable_substitution);
610*592efe25SPierre Pronchery TEST_RUN(basename, test_eval_nested_variables);
611*592efe25SPierre Pronchery TEST_RUN(basename, test_eval_multiple_variables);
612*592efe25SPierre Pronchery TEST_RUN(basename, test_eval_undefined_variable);
613*592efe25SPierre Pronchery TEST_RUN(basename, test_eval_sysroot_detection);
614*592efe25SPierre Pronchery TEST_RUN(basename, test_eval_null_args);
615*592efe25SPierre Pronchery
616*592efe25SPierre Pronchery TEST_RUN(basename, test_emit_guards);
617*592efe25SPierre Pronchery TEST_RUN(basename, test_emit_text_and_eval);
618*592efe25SPierre Pronchery TEST_RUN(basename, test_emit_var_and_eval);
619*592efe25SPierre Pronchery TEST_RUN(basename, test_emit_sysroot_and_eval);
620*592efe25SPierre Pronchery
621*592efe25SPierre Pronchery TEST_RUN(basename, test_dollar_escape);
622*592efe25SPierre Pronchery TEST_RUN(basename, test_malformed_unclosed);
623*592efe25SPierre Pronchery TEST_RUN(basename, test_empty_braces);
624*592efe25SPierre Pronchery
625*592efe25SPierre Pronchery TEST_RUN(basename, test_circular_reference);
626*592efe25SPierre Pronchery TEST_RUN(basename, test_references_var);
627*592efe25SPierre Pronchery TEST_RUN(basename, test_references_var_text_only);
628*592efe25SPierre Pronchery
629*592efe25SPierre Pronchery TEST_RUN(basename, test_compile_time_sysroot);
630*592efe25SPierre Pronchery TEST_RUN(basename, test_sysroot_dot_disables);
631*592efe25SPierre Pronchery TEST_RUN(basename, test_sysroot_root_disables);
632*592efe25SPierre Pronchery TEST_RUN(basename, test_sysroot_trailing_slash_trimmed);
633*592efe25SPierre Pronchery TEST_RUN(basename, test_sysroot_normalizes_to_root_disables);
634*592efe25SPierre Pronchery
635*592efe25SPierre Pronchery TEST_RUN(basename, test_rewrite_selfrefs);
636*592efe25SPierre Pronchery TEST_RUN(basename, test_rewrite_selfrefs_no_match);
637*592efe25SPierre Pronchery
638*592efe25SPierre Pronchery TEST_RUN(basename, test_compile_eval_roundtrip);
639*592efe25SPierre Pronchery TEST_RUN(basename, test_compile_produces_nonempty_buffer);
640*592efe25SPierre Pronchery TEST_RUN(basename, test_compile_null_args);
641*592efe25SPierre Pronchery
642*592efe25SPierre Pronchery return EXIT_SUCCESS;
643*592efe25SPierre Pronchery }
644