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