1 /*
2 * test-queue.c
3 * Tests for the public libpkgconf queue 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 #define FIXTURE_DIR "test-queue-pcdir"
21
22 static void
write_pc(const char * name,const char * contents)23 write_pc(const char *name, const char *contents)
24 {
25 char path[512];
26 FILE *f;
27
28 snprintf(path, sizeof path, "%s/%s", FIXTURE_DIR, name);
29 f = fopen(path, "wb");
30 TEST_ASSERT_NONNULL(f);
31 fwrite(contents, 1, strlen(contents), f);
32 fclose(f);
33 }
34
35 static void
setup_fixtures(void)36 setup_fixtures(void)
37 {
38 mkdir(FIXTURE_DIR, 0755);
39 write_pc("qbar.pc", "Name: qbar\nDescription: bar\nVersion: 2.0\n");
40 write_pc("qfoo.pc",
41 "Name: qfoo\nDescription: foo\nVersion: 1.0\nRequires: qbar >= 1.0\n");
42 }
43
44 static void
teardown_fixtures(void)45 teardown_fixtures(void)
46 {
47 remove(FIXTURE_DIR "/qfoo.pc");
48 remove(FIXTURE_DIR "/qbar.pc");
49 rmdir(FIXTURE_DIR);
50 }
51
52 static pkgconf_client_t *
fixture_client(void)53 fixture_client(void)
54 {
55 pkgconf_client_t *client = test_client_new();
56
57 pkgconf_path_free(&client->dir_list);
58 pkgconf_path_add(FIXTURE_DIR, &client->dir_list, false);
59
60 return client;
61 }
62
63 static size_t
required_count(const pkgconf_pkg_t * world)64 required_count(const pkgconf_pkg_t *world)
65 {
66 size_t n = 0;
67 const pkgconf_node_t *iter;
68
69 PKGCONF_FOREACH_LIST_ENTRY(world->required.head, iter)
70 {
71 n++;
72 }
73
74 return n;
75 }
76
77 static bool
contains_dep(const pkgconf_pkg_t * world,const char * name)78 contains_dep(const pkgconf_pkg_t *world, const char *name)
79 {
80 const pkgconf_node_t *iter;
81
82 PKGCONF_FOREACH_LIST_ENTRY(world->required.head, iter)
83 {
84 const pkgconf_dependency_t *dep = iter->data;
85
86 if (!strcmp(dep->package, name))
87 return true;
88 }
89
90 return false;
91 }
92
93 static void
test_queue_validate_success(void)94 test_queue_validate_success(void)
95 {
96 pkgconf_client_t *client = fixture_client();
97 pkgconf_list_t queue = PKGCONF_LIST_INITIALIZER;
98
99 pkgconf_queue_push(&queue, "qfoo");
100 TEST_ASSERT_TRUE(pkgconf_queue_validate(client, &queue, -1));
101
102 pkgconf_queue_free(&queue);
103 pkgconf_client_free(client);
104 }
105
106 static void
test_queue_validate_version_satisfied(void)107 test_queue_validate_version_satisfied(void)
108 {
109 pkgconf_client_t *client = fixture_client();
110 pkgconf_list_t queue = PKGCONF_LIST_INITIALIZER;
111
112 pkgconf_queue_push(&queue, "qbar >= 1.0");
113 TEST_ASSERT_TRUE(pkgconf_queue_validate(client, &queue, -1));
114
115 pkgconf_queue_free(&queue);
116 pkgconf_client_free(client);
117 }
118
119 static void
test_queue_validate_missing_package(void)120 test_queue_validate_missing_package(void)
121 {
122 pkgconf_client_t *client = fixture_client();
123 pkgconf_list_t queue = PKGCONF_LIST_INITIALIZER;
124
125 pkgconf_queue_push(&queue, "does-not-exist");
126 TEST_ASSERT_FALSE(pkgconf_queue_validate(client, &queue, -1));
127
128 pkgconf_queue_free(&queue);
129 pkgconf_client_free(client);
130 }
131
132 static void
test_queue_validate_unsatisfiable_version(void)133 test_queue_validate_unsatisfiable_version(void)
134 {
135 pkgconf_client_t *client = fixture_client();
136 pkgconf_list_t queue = PKGCONF_LIST_INITIALIZER;
137
138 pkgconf_queue_push(&queue, "qbar >= 99.0");
139 TEST_ASSERT_FALSE(pkgconf_queue_validate(client, &queue, -1));
140
141 pkgconf_queue_free(&queue);
142 pkgconf_client_free(client);
143 }
144
145 static void
test_queue_validate_empty(void)146 test_queue_validate_empty(void)
147 {
148 pkgconf_client_t *client = fixture_client();
149 pkgconf_list_t queue = PKGCONF_LIST_INITIALIZER;
150
151 TEST_ASSERT_FALSE(pkgconf_queue_validate(client, &queue, -1));
152
153 pkgconf_queue_free(&queue);
154 pkgconf_client_free(client);
155 }
156
157 struct apply_state {
158 int calls;
159 size_t deps;
160 bool saw_qfoo;
161 bool saw_qbar;
162 bool retval;
163 };
164
165 static bool
apply_cb(pkgconf_client_t * client,pkgconf_pkg_t * world,void * data,int maxdepth)166 apply_cb(pkgconf_client_t *client, pkgconf_pkg_t *world, void *data, int maxdepth)
167 {
168 struct apply_state *st = data;
169
170 (void) client;
171 (void) maxdepth;
172
173 st->calls++;
174 st->deps = required_count(world);
175 st->saw_qfoo = contains_dep(world, "qfoo");
176 st->saw_qbar = contains_dep(world, "qbar");
177
178 return st->retval;
179 }
180
181 static void
test_queue_apply_success(void)182 test_queue_apply_success(void)
183 {
184 pkgconf_client_t *client = fixture_client();
185 pkgconf_list_t queue = PKGCONF_LIST_INITIALIZER;
186 struct apply_state st = { .retval = true };
187
188 pkgconf_queue_push(&queue, "qfoo");
189 TEST_ASSERT_TRUE(pkgconf_queue_apply(client, &queue, apply_cb, -1, &st));
190
191 TEST_ASSERT_EQ(st.calls, 1);
192 TEST_ASSERT_GE(st.deps, 2);
193 TEST_ASSERT_TRUE(st.saw_qfoo);
194 TEST_ASSERT_TRUE(st.saw_qbar);
195
196 pkgconf_queue_free(&queue);
197 pkgconf_client_free(client);
198 }
199
200 static void
test_queue_apply_callback_failure(void)201 test_queue_apply_callback_failure(void)
202 {
203 pkgconf_client_t *client = fixture_client();
204 pkgconf_list_t queue = PKGCONF_LIST_INITIALIZER;
205 struct apply_state st = { .retval = false };
206
207 pkgconf_queue_push(&queue, "qfoo");
208 TEST_ASSERT_FALSE(pkgconf_queue_apply(client, &queue, apply_cb, -1, &st));
209 TEST_ASSERT_EQ(st.calls, 1);
210
211 pkgconf_queue_free(&queue);
212 pkgconf_client_free(client);
213 }
214
215 static void
test_queue_apply_missing_package(void)216 test_queue_apply_missing_package(void)
217 {
218 pkgconf_client_t *client = fixture_client();
219 pkgconf_list_t queue = PKGCONF_LIST_INITIALIZER;
220 struct apply_state st = { .retval = true };
221
222 pkgconf_queue_push(&queue, "does-not-exist");
223 TEST_ASSERT_FALSE(pkgconf_queue_apply(client, &queue, apply_cb, -1, &st));
224 TEST_ASSERT_EQ(st.calls, 0);
225
226 pkgconf_queue_free(&queue);
227 pkgconf_client_free(client);
228 }
229
230 int
main(int argc,char * argv[])231 main(int argc, char *argv[])
232 {
233 (void) argc;
234 const char *basename = pkgconf_path_find_basename(argv[0]);
235
236 setup_fixtures();
237
238 TEST_RUN(basename, test_queue_validate_success);
239 TEST_RUN(basename, test_queue_validate_version_satisfied);
240 TEST_RUN(basename, test_queue_validate_missing_package);
241 TEST_RUN(basename, test_queue_validate_unsatisfiable_version);
242 TEST_RUN(basename, test_queue_validate_empty);
243 TEST_RUN(basename, test_queue_apply_success);
244 TEST_RUN(basename, test_queue_apply_callback_failure);
245 TEST_RUN(basename, test_queue_apply_missing_package);
246
247 teardown_fixtures();
248
249 return EXIT_SUCCESS;
250 }
251